Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config.env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contracts:
genericArbAddress: $GENERIC_ARB_ADDRESS_V6
balancerArbAddress: $BALANCER_ARB_ADDRESS_V6
stabullArbAddress: $STABULL_ARB_ADDRESS_V6
raindexArbAddress: $RAINDEX_ARB_ADDRESS_V6
dispair: $DISPAIR_V6
liquidityProviders: $LIQUIDITY_PROVIDERS
route: $ROUTE
Expand Down Expand Up @@ -51,3 +52,4 @@ orderbookTradeTypes:
router: $ROUTER_ENABLED_ORDERBOOKS
interOrderbook: $INTER_ENABLED_ORDERBOOKS
intraOrderbook: $INTRA_ENABLED_ORDERBOOKS
raindexRouter: $RAINDEX_ENABLED_ORDERBOOKS
6 changes: 6 additions & 0 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ contracts:
balancerArbAddress: "0x1234...5678"
# Stabull arb v6 contract address
stabullArbAddress: "0x1234...5678"
# Raindex Router arb v6 contract address
raindexArbAddress: "0x1234...5678"
# Dispair v6 contract address
dispair: "0x1234...5678"

Expand Down Expand Up @@ -192,3 +194,7 @@ orderbookTradeTypes:
intraOrderbook:
- "0xabcd..."
- "0x1234..."
# Enable raindex router trades for the given orderbooks
raindexRouter:
- "0xabcd..."
- "0x1234..."
6 changes: 5 additions & 1 deletion src/common/abis/orderbook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export namespace _v6 {
`(${EvaluableV4} evaluable, ${IOV2}[] validInputs, ${IOV2}[] validOutputs, bytes32 nonce, bytes32 secret, bytes meta)` as const;
export const ClearConfigV2 =
"(uint256 aliceInputIOIndex, uint256 aliceOutputIOIndex, uint256 bobInputIOIndex, uint256 bobOutputIOIndex, bytes32 aliceBountyVaultId, bytes32 bobBountyVaultId)" as const;
export const RouteLeg = `(uint8 routeLegType, address destination, bytes data)[]`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing as const assertion on RouteLeg.

Every other struct definition in _v6 (and _v4/_v5) uses as const to preserve the literal string type, which is required for parseAbiParameters to produce a strongly-typed result. Without it, the type widens to string, and parseAbiParameters(_v6.RouteLeg) at line 262 will lose type inference.

Proposed fix
-    export const RouteLeg = `(uint8 routeLegType, address destination, bytes data)[]`;
+    export const RouteLeg = `(uint8 routeLegType, address destination, bytes data)[]` as const;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const RouteLeg = `(uint8 routeLegType, address destination, bytes data)[]`;
export const RouteLeg = `(uint8 routeLegType, address destination, bytes data)[]` as const;
🤖 Prompt for AI Agents
In `@src/common/abis/orderbook.ts` at line 124, RouteLeg is missing an "as const"
assertion so its literal ABI string widens to string and breaks type inference
when used with parseAbiParameters (e.g., parseAbiParameters(_v6.RouteLeg)). Fix
by adding the as const assertion to the RouteLeg export (the exported constant
named RouteLeg) so it preserves the literal type; ensure any other occurrences
in the _v6 ABI group follow the same pattern to keep parseAbiParameters strongly
typed.


// signatures
export const Orderbook = [
Expand Down Expand Up @@ -149,7 +150,7 @@ export namespace _v6 {
export const Arb = [
"function iRouteProcessor() external view returns (address)",
`function arb5(address orderBook, ${TakeOrdersConfigV5} calldata takeOrders, ${TaskV2} calldata task) external payable`,
// `function arb4(address orderBook, ${TakeOrdersConfigV5} calldata startTakeOrders, ${TakeOrdersConfigV5} calldata endTakeOrders, bytes calldata exchangeData, ${TaskV2} calldata task) external payable`,
`function arb4(address orderBook, ${TakeOrdersConfigV5}[] calldata startTakeOrders, bytes calldata exchangeData, ${TaskV2} calldata task) external payable`,
] as const;
Comment on lines 150 to 154
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all usages of arb4 in the codebase
rg -n --type=ts -C3 '\barb4\b' --max-count=100

Repository: rainlanguage/rain.solver

Length of output: 8743


🏁 Script executed:

#!/bin/bash
# Search for any references to endTakeOrders to ensure it's been removed from all call sites
rg -n 'endTakeOrders' --type=ts

Repository: rainlanguage/rain.solver

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Also check the actual file to confirm the lines mentioned
sed -n '145,160p' src/common/abis/orderbook.ts

Repository: rainlanguage/rain.solver

Length of output: 1273


🏁 Script executed:

#!/bin/bash
# Get full context around arb4 calls in router simulate
sed -n '270,280p' src/core/modes/router/simulate.ts

Repository: rainlanguage/rain.solver

Length of output: 478


🏁 Script executed:

#!/bin/bash
# Get full context around arb4 calls in inter simulate
sed -n '556,566p' src/core/modes/inter/simulate.ts

Repository: rainlanguage/rain.solver

Length of output: 351


🏁 Script executed:

#!/bin/bash
# Verify which ABI versions exist and their arb4 signatures
grep -n "export.*Arb.*=" src/common/abis/orderbook.ts

Repository: rainlanguage/rain.solver

Length of output: 593


🏁 Script executed:

#!/bin/bash
# Get more context around inter/simulate.ts arb4 call to see how args is constructed
sed -n '545,565p' src/core/modes/inter/simulate.ts

Repository: rainlanguage/rain.solver

Length of output: 788


🏁 Script executed:

#!/bin/bash
# Get the full V5 arb4 ABI definition to confirm the signature
sed -n '150,154p' src/common/abis/orderbook.ts

Repository: rainlanguage/rain.solver

Length of output: 472


🏁 Script executed:

#!/bin/bash
# Check router/simulate.ts context for what takeOrdersConfigStruct contains
sed -n '260,282p' src/core/modes/router/simulate.ts

Repository: rainlanguage/rain.solver

Length of output: 952


Breaking ABI change to arb4: startTakeOrders is now an array, endTakeOrders is removed, and new exchangeData parameter is required.

This is a significant contract interface change. The new V5 arb4 signature requires four parameters: orderBook, startTakeOrders[], exchangeData, and task. However, call sites in src/core/modes/router/simulate.ts (line 274) and src/core/modes/inter/simulate.ts (line 561) are still passing only three arguments and missing the exchangeData parameter. Update these call sites to include the required exchangeData parameter, and update corresponding tests to reflect the new four-argument signature.

🤖 Prompt for AI Agents
In `@src/common/abis/orderbook.ts` around lines 150 - 154, The ABI for Arb.arb4
changed to require four args (orderBook, startTakeOrders[], exchangeData, task);
update all call sites that invoke arb4 (notably in the router and inter simulate
functions) to pass the new exchangeData parameter between startTakeOrders and
task, e.g., modify the calls in simulate.ts where arb4 is invoked to include an
appropriate bytes exchangeData value (or an empty bytes if no data) as the third
argument; also update any unit/integration tests that invoke arb4 to reflect the
new four-argument signature and adjust expected behavior accordingly.

}

Expand Down Expand Up @@ -257,6 +258,8 @@ export namespace OrderbookAbi {

/** Order v4 (for orderbook v6) struct ABI */
export const OrderStructAbi = parseAbiParameters(_v6.OrderV4);

export const RouteLeg = parseAbiParameters(_v6.RouteLeg);
}

/** Orderbook v4 structs */
Expand All @@ -273,6 +276,7 @@ export namespace OrderbookAbi {
export const TakeOrdersConfig = _v6.TakeOrdersConfigV5;
export const ClearConfig = _v6.ClearConfigV2;
export const Quote = _v6.QuoteV2;
export const RouteLeg = _v6.RouteLeg;
}

/** Signature ABI for Orderbook v4 and Arb contracts */
Expand Down
311 changes: 311 additions & 0 deletions src/config/validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,317 @@ describe("Test yaml Validator methods", async function () {
"unexpected error",
);
});

it("test Validator resolveContracts", async function () {
const sushiArbV4 = `0x${"1".repeat(40)}`;
const dispairV4 = `0x${"2".repeat(40)}`;
const genericArbV4 = `0x${"3".repeat(40)}`;
const balancerArbV4 = `0x${"4".repeat(40)}`;
const stabullArbV4 = `0x${"5".repeat(40)}`;

const sushiArbV5 = `0x${"6".repeat(40)}`;
const dispairV5 = `0x${"7".repeat(40)}`;
const genericArbV5 = `0x${"8".repeat(40)}`;
const balancerArbV5 = `0x${"9".repeat(40)}`;
const stabullArbV5 = `0x${"a".repeat(40)}`;

const sushiArbV6 = `0x${"b".repeat(40)}`;
const dispairV6 = `0x${"c".repeat(40)}`;
const genericArbV6 = `0x${"d".repeat(40)}`;
const balancerArbV6 = `0x${"e".repeat(40)}`;
const stabullArbV6 = `0x${"f".repeat(40)}`;
const raindexArbV6 = `0x${"1".repeat(39)}1`;

// happy path: all v4 contracts provided
let input: any = {
contracts: {
v4: {
sushiArbAddress: sushiArbV4,
dispair: dispairV4,
genericArbAddress: genericArbV4,
balancerArbAddress: balancerArbV4,
stabullArbAddress: stabullArbV4,
},
},
};
let result = Validator.resolveContracts(input);
let expected: any = {
v4: {
sushiArb: sushiArbV4 as `0x${string}`,
dispair: dispairV4 as `0x${string}`,
genericArb: genericArbV4 as `0x${string}`,
balancerArb: balancerArbV4 as `0x${string}`,
stabullArb: stabullArbV4 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// happy path: all v5 contracts provided
input = {
contracts: {
v5: {
sushiArbAddress: sushiArbV5,
dispair: dispairV5,
genericArbAddress: genericArbV5,
balancerArbAddress: balancerArbV5,
stabullArbAddress: stabullArbV5,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v5: {
sushiArb: sushiArbV5 as `0x${string}`,
dispair: dispairV5 as `0x${string}`,
genericArb: genericArbV5 as `0x${string}`,
balancerArb: balancerArbV5 as `0x${string}`,
stabullArb: stabullArbV5 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// happy path: all v6 contracts provided including raindex
input = {
contracts: {
v6: {
sushiArbAddress: sushiArbV6,
dispair: dispairV6,
genericArbAddress: genericArbV6,
balancerArbAddress: balancerArbV6,
stabullArbAddress: stabullArbV6,
raindexArbAddress: raindexArbV6,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v6: {
sushiArb: sushiArbV6 as `0x${string}`,
dispair: dispairV6 as `0x${string}`,
genericArb: genericArbV6 as `0x${string}`,
balancerArb: balancerArbV6 as `0x${string}`,
stabullArb: stabullArbV6 as `0x${string}`,
raindexArb: raindexArbV6 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// happy path: mix of v4, v5, and v6 contracts
input = {
contracts: {
v4: {
sushiArbAddress: sushiArbV4,
dispair: dispairV4,
},
v5: {
genericArbAddress: genericArbV5,
},
v6: {
balancerArbAddress: balancerArbV6,
raindexArbAddress: raindexArbV6,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v4: {
sushiArb: sushiArbV4 as `0x${string}`,
dispair: dispairV4 as `0x${string}`,
genericArb: undefined,
balancerArb: undefined,
stabullArb: undefined,
},
v5: {
sushiArb: undefined,
dispair: undefined,
genericArb: genericArbV5 as `0x${string}`,
balancerArb: undefined,
stabullArb: undefined,
},
v6: {
sushiArb: undefined,
dispair: undefined,
genericArb: undefined,
balancerArb: balancerArbV6 as `0x${string}`,
stabullArb: undefined,
raindexArb: raindexArbV6 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// happy path: partial v4 contracts
input = {
contracts: {
v4: {
sushiArbAddress: sushiArbV4,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v4: {
sushiArb: sushiArbV4 as `0x${string}`,
dispair: undefined,
genericArb: undefined,
balancerArb: undefined,
stabullArb: undefined,
},
};
assert.deepEqual(result, expected);

// happy path: partial v5 contracts
input = {
contracts: {
v5: {
dispair: dispairV5,
balancerArbAddress: balancerArbV5,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v5: {
sushiArb: undefined,
dispair: dispairV5 as `0x${string}`,
genericArb: undefined,
balancerArb: balancerArbV5 as `0x${string}`,
stabullArb: undefined,
},
};
assert.deepEqual(result, expected);

// happy path: partial v6 contracts
input = {
contracts: {
v6: {
raindexArbAddress: raindexArbV6,
},
},
};
result = Validator.resolveContracts(input);
expected = {
v6: {
sushiArb: undefined,
dispair: undefined,
genericArb: undefined,
balancerArb: undefined,
stabullArb: undefined,
raindexArb: raindexArbV6 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// happy path: no contracts provided (empty object returned)
input = {};
result = Validator.resolveContracts(input);
expected = {};
assert.deepEqual(result, expected);

// happy path: contracts object exists but no versions provided
input = { contracts: {} };
result = Validator.resolveContracts(input);
expected = {};
assert.deepEqual(result, expected);

// happy path: using env variables for v4
process.env.SUSHI_ARB_V4 = sushiArbV4;
process.env.DISPAIR_V4 = dispairV4;
input = {
contracts: {
v4: {
sushiArbAddress: "$SUSHI_ARB_V4",
dispair: "$DISPAIR_V4",
},
},
};
result = Validator.resolveContracts(input);
expected = {
v4: {
sushiArb: sushiArbV4 as `0x${string}`,
dispair: dispairV4 as `0x${string}`,
genericArb: undefined,
balancerArb: undefined,
stabullArb: undefined,
},
};
assert.deepEqual(result, expected);

// happy path: using env variables for v6 including raindex
process.env.RAINDEX_ARB_V6 = raindexArbV6;
process.env.BALANCER_ARB_V6 = balancerArbV6;
input = {
contracts: {
v6: {
raindexArbAddress: "$RAINDEX_ARB_V6",
balancerArbAddress: "$BALANCER_ARB_V6",
},
},
};
result = Validator.resolveContracts(input);
expected = {
v6: {
sushiArb: undefined,
dispair: undefined,
genericArb: undefined,
balancerArb: balancerArbV6 as `0x${string}`,
stabullArb: undefined,
raindexArb: raindexArbV6 as `0x${string}`,
},
};
assert.deepEqual(result, expected);

// unhappy: invalid address format for v4 sushiArbAddress
input = {
contracts: {
v4: {
sushiArbAddress: "invalid_address",
},
},
};
assert.throws(
() => Validator.resolveContracts(input),
"expected valid sushiArbAddress v4 contract address",
);

// unhappy: invalid address format for v5 dispair
input = {
contracts: {
v5: {
dispair: "0x123", // too short
},
},
};
assert.throws(
() => Validator.resolveContracts(input),
"expected valid dispair v5 contract address",
);

// unhappy: invalid address format for v6 raindexArbAddress
input = {
contracts: {
v6: {
raindexArbAddress: "not_an_address",
},
},
};
assert.throws(
() => Validator.resolveContracts(input),
"expected valid raindexArbAddress v6 contract address",
);

// unhappy: undefined env variable
delete process.env.UNDEFINED_CONTRACT;
input = {
contracts: {
v4: {
sushiArbAddress: "$UNDEFINED_CONTRACT",
},
},
};
result = Validator.resolveContracts(input);
expected = {};
assert.deepEqual(result, expected);
});
});

describe("Test yaml Validator helpers", async function () {
Expand Down
Loading
Loading