Problem
exchange_router declares a dedicated error variant for an oversized batch:
BatchSizeLimitExceeded = 5,
but multicall (contracts/exchange_router/src/lib.rs, ~lines 361-395) never checks actions.len() against any bound before dispatching:
pub fn multicall(env: Env, caller: Address, actions: Vec<RouterAction>) -> Vec<BytesN<32>> {
caller.require_auth();
...
let len = actions.len();
let mut i = 0u32;
while i < len {
let action = actions.get(i).unwrap();
match action { ... }
i += 1;
}
results
}
grep -n "BatchSizeLimitExceeded" contracts/exchange_router/src/lib.rs matches only the declaration — the error is never constructed or raised anywhere. This is the sibling entrypoint to order_handler::create_orders, which does enforce a batch cap (if requests.len() > 5 { panic_with_error!(&env, Error::BatchSizeLimitExceeded); }, itself tracked separately for using a bare literal instead of a named constant) — multicall's own identically-named error variant suggests the same protection was intended here but was never implemented.
Why it matters
An unbounded actions vector means a single multicall transaction can chain an arbitrary number of SendTokens/CreateDeposit/CreateOrder/etc. actions in one call, each iterating the same DataStore/vault/handler cross-contract calls as the standalone equivalents. Soroban's own per-transaction resource budget is the only backstop — there's no protocol-level cap the way order_handler::create_orders deliberately has one, despite exchange_router having defined (and presumably intended to use) the identical error code for exactly this case.
Scope
In scope
contracts/exchange_router/src/lib.rs::multicall — add the missing size check.
Out of scope
order_handler::create_orders's own cap, already enforced (tracked separately only for its magic-number style).
Suggested fix
Add a bound check at the start of multicall, e.g. if actions.len() > MAX_MULTICALL_BATCH_SIZE { panic_with_error!(&env, Error::BatchSizeLimitExceeded); }, choosing a sane cap consistent with the resource budget a full batch of the most expensive action type (e.g. CreateOrder) can afford.
Verification
cargo test -p exchange-router
Add a test constructing a batch larger than the chosen cap and asserting multicall reverts with BatchSizeLimitExceeded.
Problem
exchange_routerdeclares a dedicated error variant for an oversized batch:but
multicall(contracts/exchange_router/src/lib.rs, ~lines 361-395) never checksactions.len()against any bound before dispatching:grep -n "BatchSizeLimitExceeded" contracts/exchange_router/src/lib.rsmatches only the declaration — the error is never constructed or raised anywhere. This is the sibling entrypoint toorder_handler::create_orders, which does enforce a batch cap (if requests.len() > 5 { panic_with_error!(&env, Error::BatchSizeLimitExceeded); }, itself tracked separately for using a bare literal instead of a named constant) —multicall's own identically-named error variant suggests the same protection was intended here but was never implemented.Why it matters
An unbounded
actionsvector means a singlemulticalltransaction can chain an arbitrary number ofSendTokens/CreateDeposit/CreateOrder/etc. actions in one call, each iterating the same DataStore/vault/handler cross-contract calls as the standalone equivalents. Soroban's own per-transaction resource budget is the only backstop — there's no protocol-level cap the wayorder_handler::create_ordersdeliberately has one, despiteexchange_routerhaving defined (and presumably intended to use) the identical error code for exactly this case.Scope
In scope
contracts/exchange_router/src/lib.rs::multicall— add the missing size check.Out of scope
order_handler::create_orders's own cap, already enforced (tracked separately only for its magic-number style).Suggested fix
Add a bound check at the start of
multicall, e.g.if actions.len() > MAX_MULTICALL_BATCH_SIZE { panic_with_error!(&env, Error::BatchSizeLimitExceeded); }, choosing a sane cap consistent with the resource budget a full batch of the most expensive action type (e.g.CreateOrder) can afford.Verification
cargo test -p exchange-routerAdd a test constructing a batch larger than the chosen cap and asserting
multicallreverts withBatchSizeLimitExceeded.