Skip to main content
A settlement moves one asset (asset_out) from the protocol vault to a target and brings another (asset_in) back, atomically, without a withdrawal. Market makers use settlements to clear exposure from Tplus fills through onchain venues; the onchain leg behaves like a flash loan against vault funds. Two parties: the user, whose Tplus balance is debited and credited, and a settler, an approved executor who runs the onchain transaction. Settlers are approved per-chain; the list is readable onchain via getApprovedSettlers().

Flow

  1. Request and margin check. The request (POST /settlement/init) specifies asset out/in, amounts, sub-account, mode, and the settler. Validation: asset_in must differ from asset_out, and the mode (Margin or Spot) must match the sub-account type. The clearing engine simulates an initial-margin check at both oracle and mark price: the account must pass IM and the settlement, including the rebalancing fee, must not decrease the IM surplus. Accounts with no borrows always pass.
  2. Lock. The outgoing amount is moved into a settlement lock keyed by the request’s chain and nonce.
  3. Approval. The protocol signs a time-bound approval. The approval is returned by POST /settlement/init; outstanding user approvals can be fetched with GET /settlement/signatures/{user_id}.
  4. Execution. The settler calls executeAtomicSettlement(...) on the vault within the validity window (10 seconds from approval). The vault verifies signature, nonce, and expiry.
  5. Confirm or expire. A confirmed settlement event credits asset_in to the user. If the transaction does not land in time, or reverts, which emits no event, the lock expires, funds are restored, and the vault rejects late execution because validUntil has passed.
The margin check compares simulated initial-margin surplus before and after the settlement:
For a simple USD-out, ETH-in settlement:
Example: an account sends out 1,000 USDC, pays a 10 USD rebalancing fee, and receives 0.6 ETH. If ETH = 2,000 USD and im_factor = 90%, the incoming ETH contributes 0.6 * 2,000 USD * 90% = 1,080 USD to IM surplus, so delta_IM_surplus = 1,080 USD - 1,000 USD - 10 USD = +70 USD and the settlement passes. If the account receives only 0.5 ETH, the contribution is 900 USD, delta_IM_surplus = -110 USD, and the request is rejected before any lock.

Utilization caps

Each asset’s liquidity pool has a utilization cap (max_utilization, a per-asset onchain risk parameter). Utilization is the share of the asset’s deposited liquidity that is currently borrowed, and the cap bounds how far the pool can be drawn down. When an asset sits at its cap, the protocol will not let it leave the vault through the live single-settlement path. A settlement that would move a capped asset out is blocked until utilization falls. Batch settlement and delta squashing are planned, but not available through the public production flow today. The vault ABI includes a squashing entrypoint for batch settlement (executeSquashingSettlements). In that planned flow, a settler would execute several settlements as one batch and the vault would move only the net of each token across the batch instead of every gross leg:
  • net < 0: the batch sends more of the token out than it brings in; the vault pushes only the difference to the settler.
  • net > 0: the batch brings more in than it sends out; the vault pulls only the difference from the settler.
  • net = 0: the legs cancel and the token never moves.
A token both bought and sold inside one batch would net out and never cross the vault boundary. That is the planned mechanism for settling capped assets when the batch nets to zero or a net inflow for that asset. It is not available through the public production flow today. Squashing nets vault movements, not obligations. Each settlement is honored on its own terms. The settler must commit, per settlement, to deliver at least that settlement’s amountIn (one user’s leg cannot subsidize another’s); the vault emits a separate Settled event per settlement with the real amounts; and solvency checks, locks, and nonces all operate on the real per-settlement amounts, not the net. Current public availability: use single atomic settlements through POST /settlement/init and executeAtomicSettlement. Do not build an integration that depends on batch or squashing submission until that path is published.

Integrator details

  • Nonces are independent sequences per user, per chain, and per action type (deposit / withdrawal / settlement); settlement nonces are additionally scoped per sub-account. Do not assume cross-channel ordering. Onchain counters: depositCounts(user), withdrawalCounts(user), settlementCounts(user, account).
  • An explicit settlement nonce must be >= the onchain counter and must not collide with an in-flight lock. If omitted, the next free nonce is assigned.
  • Approvals are returned by POST /settlement/init; users fetch outstanding settlement signatures via GET /settlement/signatures/{user_id}.
  • Vault revert reasons: Expired, SettlerNotApproved, NotExecutor, InvalidSignature, InvalidNonce(expected, given), TransferFailed, TransferFromFailed, InsufficientAmountFromExecutor.
  • POST /settlement/init is rejected before any lock if the account does not pass IM, the settlement would decrease the IM surplus, a protocol open-interest or collateral cap would breach, the oracle is stale or the asset is circuit-broken, or asset_in equals asset_out.