Batch Settlement
Batch fill inputs, outputs, tokenisation callbacks and router execution constraints.
SpectraLimitOrderBatchFiller fills several orders in one call using per-token
budgets supplied by its caller. It calls the 1inch settlement once per order.
Direct fills send maker assets to the caller; tokenised fills first send them
to SpectraLimitOrderTokeniser to mint or redeem the market's PT/YT pair.
The order reference defines the signed order, pricing and cancellation interfaces. The order-book HTTP reference defines order publication, listing and quote formats.
Protect the taker's final output. Settlement enforces the maker's quote,
not the taker's final proceeds after conversions on the maker's underlying
or vault-share leg, tokenisation or later AMM swaps. Always include
ASSERT_MIN_BALANCE in the final receive token,
after all swaps and output conversions and before transferring the output.
Per-fill thresholds and minMakingFilled do not replace this final check.
Batch filler
The batch filler is the settlement's immediate taker, including when called through the router. It pulls each budget once, executes the fills in order, then refunds remaining budget-token balances to its caller.
Read contract addresses
These read-only functions take no arguments and each return one address,
set by the batch filler's constructor:
| Function | Address returned and its role |
|---|---|
LIMIT_ORDER_PROTOCOL() | The settlement called for each order in fillMany. |
TOKENISER() | The conversion contract used for tokenised fills and allowed to draw from the batch's budgets. |
Data structures
Order refers to the signed-order ABI tuple.
The layouts below expand TakerTraits to its ABI type, uint256, and
IERC20 to address.
struct Fill {
Order order;
bytes signature;
bool contractMaker;
uint256 amount;
uint256 takerTraits;
bytes extension;
bytes interaction;
uint256 gasLimit;
}
struct Budget {
address token;
uint256 amount;
}Fill field | Meaning and constraint |
|---|---|
order | Signed Order. Maker allowed-sender bits must be zero. |
signature | With contractMaker = false, exactly 64 bytes containing compact r || vs. Otherwise, the ERC-1271 signature bytes. |
contractMaker | Selects fillContractOrderArgs when true; otherwise selects fillOrderArgs. |
amount | Maker-token base units in maker-amount mode; taker-token base units otherwise, as selected by takerTraits. |
takerTraits | Amount mode, threshold and other taker flags. Target and Permit2 bits must be clear; length fields are overwritten. |
extension | The order's extension bytes. |
interaction | Empty, another taker interaction, or the packed tokeniser payload described below. Other interaction addresses receive no budget-token approvals. |
gasLimit | Gas units forwarded to this fill's settlement call, including signature checks, hooks, transfers and tokeniser execution. |
Budget field | Meaning and constraint |
|---|---|
token | Token approved by the caller to the batch filler. Entries must be strictly ascending by token address, without duplicates. |
amount | Amount pulled from the caller, in that token's base units. May be zero for a token produced during tokenisation. |
fillMany
Executes the batch and returns aggregate fill results and the caller's net contribution for each budget.
function fillMany(
Fill[] calldata fills,
Budget[] calldata budgets,
uint256 minMakingFilled,
bool skipReverts
) external returns (uint256 totalMaking, uint256 filled, uint256[] memory spent);| Input | Meaning and unit |
|---|---|
fills | Nonempty array of fills, processed in input order. |
budgets | Token/amount pairs. Every order's taker asset must have an entry, including a zero-amount entry when tokenisation produces it. |
minMakingFilled | Minimum sum of successful making amounts, each in its own maker token's base units. |
skipReverts | Whether to skip failed settlement calls. Does not bypass batch validation or the minimum-making check. |
| Return | Meaning and unit |
|---|---|
totalMaking | Sum of actual successful making amounts, each in its own maker token's base units. |
filled | Number of successful fills. |
spent | Per-budget caller contribution after refunds, in input budget order and that token's base units: max(budget.amount - refundedBalance, 0). |
totalMaking and minMakingFilled can combine different token units.
They are not common-denomination values or a minimum final output. Use a
separate output constraint after any conversions.
Execution constraints
Choose each gasLimit from a simulation with a margin.
The transaction also needs gas for encoding, budget transfers, approvals,
looping, refunds and EIP-150 forwarding overhead. Merely summing the caps is
not sufficient. A settlement that exhausts its cap follows skipReverts;
insufficient remaining gas to forward that cap reverts the entire batch
with InsufficientGas(index), even when skipping failures is enabled.
Private OTC orders: use direct settlement.
Do not authorize the BatchFiller as a taker for private OTC orders or use it
to fill them. Fill private OTC orders directly through LimitOrderProtocol.
All budget ordering and fill-shape checks complete before tokens are pulled or approved. The caller must approve each funded budget token to the filler. The filler approves budget tokens to the settlement and, for tokenised batches, the tokeniser; those approvals are reset before returning.
The filler replaces the extension/interaction length fields in takerTraits
and adds its own target: the caller for direct fills, or the tokeniser for
tokenised fills. Other traits, including amount mode and threshold, pass
through. A tokenised fill's recipient must equal the caller of fillMany.
skipReverts = true skips only failed settlement calls. Validation,
token pulls and approvals, the final minMakingFilled check and refunds
remain mandatory. With skipReverts = false, a failed settlement call
reverts as FillFailed(index, reason).
Events
event FillSkipped(address indexed caller, uint256 index, bytes reason);
event BatchFilled(address indexed caller, uint256 filled, uint256 totalMaking);FillSkipped.index identifies the input fill. Its reason contains at most
four bytes of settlement revert data: the error selector without arguments,
or fewer bytes if less data was returned. FillFailed retains the full reason
when skipReverts is false. BatchFilled follows the minimum-making check
and refunds. Individual settlement events identify each order's fill outcome.
Failures
| Error | Cause |
|---|---|
EmptyBatch() | fills is empty. |
DuplicateBudget(address token) | Adjacent budget entries use the same token. |
UnsortedBudgets(address previousToken, address token) | A budget token sorts below its predecessor. |
TakerAssetNotBudgeted(uint256 index, address takerAsset) | An order's taker asset has no budget entry. |
UnsupportedTakerTraits(uint256 index) | A supplied target or Permit2 bit is set. |
InvalidSignatureLength(uint256 index) | An EOA-maker signature is not 64 bytes. |
SenderRestrictedOrder(uint256 index) | Maker allowed-sender bits are nonzero. |
InvalidTokenisation(uint256 index) | A configured-tokeniser interaction is not 148 bytes or names a recipient other than the caller. |
InsufficientGas(uint256 index) | Not enough gas remains to forward the fill's exact cap. Reverts the whole batch. |
FillFailed(uint256 index, bytes reason) | A settlement call failed and skipReverts is false. |
InsufficientMakingFilled(uint256 filled, uint256 minimum) | The raw making-amount sum is below minMakingFilled; filled here is that sum, not the successful-fill count. |
Token transfer, approval and reentrancy-guard failures can also revert the
batch. ZeroAddress() applies to zero settlement/tokeniser addresses at
construction.
Tokeniser callback
SpectraLimitOrderTokeniser converts within one registered market during a
settlement fill. It receives the maker asset, mints IBT into PT/YT or redeems
PT/YT into IBT, supplies the maker payment and sends the proceeds to the
specified recipient.
Read contract addresses
These read-only functions take no arguments and each return one address,
set by the tokeniser's constructor:
| Function | Address returned and its role |
|---|---|
LIMIT_ORDER_PROTOCOL() | The only settlement allowed to invoke takerInteraction. |
REGISTRY() | The registry used to check that the conversion's PT belongs to a registered market. |
Interaction arguments
A tokenised fill's interaction is the packed 20-byte configured tokeniser
address followed by abi.encode(Args), with no function selector:
struct Args {
address pt;
address recipient;
uint256 input;
uint256 minOut;
}| Field | Meaning and unit |
|---|---|
pt | PT registered in the tokeniser's registry; identifies the market. |
recipient | Receives the complementary derivative, conversion surplus and leftovers. In a batch, must be the actual caller of fillMany. |
input | Conversion input pulled from the settlement's taker: IBT base units for minting, or complementary PT/YT base units for redemption. |
minOut | Minimum complementary derivative shares when minting; minimum IBT left after reserving the maker payment when redeeming. In the respective token's base units. |
Args occupies 128 bytes; the complete interaction occupies 148 bytes.
The taker's conversion input and proceeds use canonical IBT, PT or YT.
Underlying/vault-share conversions occur on the maker's quote leg. The
tokeniser supplies the exact settlement takingAmount, including any
applicable fee; the fee taker then splits fee-order payment between the maker
and collector. The taker bears conversion shortfalls and rounding.
takerInteraction
The settlement invokes this callback after transferring the maker asset and
before collecting the taker asset. Only the configured settlement may call
it. Order uses the same signed-order ABI tuple as Fill.order.
function takerInteraction(
Order calldata order,
bytes calldata extension,
bytes32 orderHash,
address taker,
uint256 makingAmount,
uint256 takingAmount,
uint256 remainingMakingAmount,
bytes calldata extraData
) external;| Input | Use in the tokeniser |
|---|---|
order | Its maker/taker assets select the mint or redeem path for the registered market. |
taker | Source of Args.input and recipient of the exact maker-payment token amount for settlement to collect. In a batch, the batch filler. |
takingAmount | Exact settlement payment, in taker-token base units. Includes the fee for fee orders; the maker receives the fee-free share. |
extraData | The 128-byte ABI-encoded Args, excluding the 20-byte interaction address. |
extension, orderHash, makingAmount, remainingMakingAmount | Supplied by settlement; unused by this tokeniser. |
For underlying-quoted redemptions, the tokeniser calls
IBT.withdraw(takingAmount, taker, address(this)) first, measures the IBT
balance decrease, then checks the remaining redeemed IBT against minOut.
A failed check rolls back the withdrawal and the complete fill. A withdrawal
that cannot execute propagates the IBT's own revert.
The callback returns no value and defines no dedicated fill event. It
resets conversion approvals and sends remaining market-token balances to
recipient; these sweeps can include underlying or vault-share residue.
Failures
| Error | Cause |
|---|---|
OnlySettlement() | Caller is not the configured settlement. |
UnregisteredPT(address pt) | Args.pt is not registered. |
UnsupportedPair(address makerAsset, address takerAsset) | The order does not exchange the market's PT/YT against a supported quote token. |
InsufficientOutput(uint256 output, uint256 required) | Conversion cannot cover the maker payment. Units are the converted PT/YT, IBT or unwrapped vault share, according to the path. |
ProceedsBelowMinimum(uint256 proceeds, uint256 minOut) | Complementary shares or net IBT proceeds fall below Args.minOut. |
Malformed ABI data, external conversions, token transfers and the reentrancy
guard can also revert. ZeroAddress() applies to zero settlement/registry
addresses at construction. A callback revert fails its settlement call and
therefore follows the batch's skipReverts setting.
Router commands
The router keeps fill outputs and budget refunds for later commands. It
resolves budget amounts, approves the filler, calls fillMany, then resets
those allowances. Conversions into an order's payment token precede the fill;
conversions into the desired output token follow it.
FILL_1INCH_LIMIT_ORDERS (0x27)
The command invokes fillOneInchOrders with this ABI-encoded input:
abi.encode(Fill[] fills, Budget[] budgets,
uint256 minMakingFilled, bool skipReverts)Inputs and (totalMaking, filled, spent) returns follow fillMany, with
two router-specific constraints:
| Input | Router behavior |
|---|---|
Budget.amount | CONTRACT_BALANCE (1 << 255) resolves to the router's entire current balance of that token. Other values remain amounts in token base units. The batch filler itself does not interpret this sentinel. |
Tokeniser Args.recipient | Must be the actual router address, the caller seen by the filler. Router address sentinels are not resolved inside tokeniser arguments. |
Only the active execution's sender or the router may invoke
fillOneInchOrders; other calls revert with OnlyDuringExecution().
It emits no additional fill event. The batch and settlement provide the
fill events and failures.
ASSERT_MIN_BALANCE (0x0f)
Place the final output check after output conversions and before output transfers. The command takes:
abi.encode(address token, address owner, uint256 minValue)| Input | Meaning and unit |
|---|---|
token | ERC-20 token whose balance is checked. |
owner | Account checked; ADDRESS_THIS (address(0xe0)) selects the router. |
minValue | Minimum balance in token base units. |
The command returns no value. It reads balanceOf(owner) and reverts as
MinimumBalanceNotReached(token, owner, actualBalance, requiredMinimum)
when the balance is below minValue.
This is an absolute balance, not the increase generated by the
execution. Existing residue can contribute to the minimum. Budget limits,
per-fill thresholds and minMakingFilled do not replace this output
constraint; whole-balance conversions and transfers also include existing
residue.
TRANSFER (0x02)
Return the final output and all remaining route assets to the recipient. Rounding, conversions and skipped fills can leave dust in PT, YT, IBT, underlying or vault-share tokens. Batch refunds return to the router when it is the batch caller; they do not automatically reach the end user.
abi.encode(address token, address recipient, uint256 value)| Input | Meaning and unit |
|---|---|
token | ERC-20 token to transfer. |
recipient | Output or refund recipient; router address sentinels are resolved. |
value | Amount in token base units. CONTRACT_BALANCE (1 << 255) transfers the router's entire current balance of this token. |
After the final output assertion, use TRANSFER for the output and for
each other token the route may leave behind. The command returns no value
and emits no router-specific event. An invalid recipient or token-transfer
failure reverts the transaction. Zero resolved amounts are skipped.
fills / swaps / output conversions
→ ASSERT_MIN_BALANCE(finalToken, ADDRESS_THIS, minOut)
→ TRANSFER(finalToken, recipient, CONTRACT_BALANCE)
→ TRANSFER(each remaining route token, recipient, CONTRACT_BALANCE)