# Batch Settlement



`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](/docs/integration-reference/evm-limit-orders/orders)
defines the signed order, pricing and cancellation interfaces. The
[order-book HTTP reference](/docs/integration-reference/evm-limit-orders/order-book-api)
defines order publication, listing and quote formats.

<Callout type="warn">
  **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`](#assert_min_balance-0x0f) 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.
</Callout>

## Batch filler [#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 [#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 [#data-structures]

`Order` refers to the [signed-order ABI tuple](/docs/integration-reference/evm-limit-orders/orders#order).
The layouts below expand `TakerTraits` to its ABI type, `uint256`, and
`IERC20` to `address`.

```solidity
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](/docs/integration-reference/evm-limit-orders/orders#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` [#fillmany]

Executes the batch and returns aggregate fill results and the caller's net
contribution for each budget.

```solidity
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 [#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.

<Callout type="warn">
  **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`.
</Callout>

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 [#events]

```solidity
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 [#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 [#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 [#read-contract-addresses-1]

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 [#interaction-arguments]

A tokenised fill's `interaction` is the packed 20-byte configured tokeniser
address followed by `abi.encode(Args)`, with no function selector:

```solidity
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` [#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`.

```solidity
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 [#failures-1]

| 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 [#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`) [#fill_1inch_limit_orders-0x27]

The command invokes `fillOneInchOrders` with this ABI-encoded input:

```text
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`) [#assert_min_balance-0x0f]

Place the final output check after output conversions and before output
transfers. The command takes:

```text
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`) [#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.

```text
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.

```text
fills / swaps / output conversions
→ ASSERT_MIN_BALANCE(finalToken, ADDRESS_THIS, minOut)
→ TRANSFER(finalToken, recipient, CONTRACT_BALANCE)
→ TRANSFER(each remaining route token, recipient, CONTRACT_BALANCE)
```
