Settlement and cancellation

What the settlement contract checks for itself before any token moves, and the two ways to cancel an order.

  1. Marketenabled, epoch MATCHED, not settled, oracle fresh, price inside the band
  2. Ordertokens, epoch, validity window, receiver
  3. Replaynonce unused, then spent
  4. SignatureECDSA, else ERC-1271 with bounded gas
  5. Commitmentleaf under the root anchored before decryption
  6. Fillsize, minimum fill, partial-fill flag
  7. Pricelimit price and the order's own oracle bound
  8. Balancebase in equals base out, balances grew by exactly what was pulled
  9. Resultcomputed hash equals the hash published before settling
Every fill passes these checks inside one settleMarket call. Any failure reverts the whole market and moves nothing.

7. Settlement#

KasumiSettlement.settleMarket(epochId, marketId, clearingPrice, fills) settles one market of one epoch. The matcher supplies the price and, per order, the signed order, its signature, the base fill, and the Merkle proof of its leaf. The contract computes every token amount itself.

It reverts unless all of the following hold:

  • caller is the matcher, the market is enabled, this market of this epoch is not settled yet
  • the epoch is MATCHED
  • the oracle price is non-zero and no older than the market's maxOracleAge
  • |clearingPrice - oracle| * 10000 <= oracle * market.maxOracleDeviationBps
  • fills are strictly ascending by order commitment (canonical order, and no duplicates)
  • for each order:
    • tokens match the market, epochId matches, side is 0 or 1
    • receiver is neither the zero address nor the settlement contract
    • sequence is below the committed orderCount
    • validAfter <= now <= validUntil
    • the nonce bit is clear; it is then set
    • the signature is valid for owner under this contract's domain. A 65-byte signature is checked with ECDSA first (low s, v of 27 or 28), so an EOA that has delegated its code under EIP-7702 still signs as an EOA. Otherwise, if owner has code, ERC-1271 is used with a gas cap of 200,000 and exactly one word of return data read.
    • the leaf (epochId, sequence, commitment, ciphertextHash) is under the epoch root
    • 0 < baseFilled <= baseAmount, equals baseAmount if partial fills are not allowed, and is at least minFillBase
    • BUY: clearingPrice <= limitPrice; SELL: clearingPrice >= limitPrice
    • if the order set maxOracleDeviationBps: a BUY does not pay more than oracle * (1 + d), a SELL does not receive less than oracle * (1 - d)
  • base pulled from sellers equals base owed to buyers
  • the result hash computed from the transfers actually performed equals the hash the matcher published for this market (ResultHashMismatch otherwise). The matcher cannot publish one result and settle another, and cannot settle a market it did not publish.
  • after pulling, the contract's balance of each token rose by exactly the amount pulled. This rejects fee-on-transfer and rebasing behaviour.

Buyers pay ceil(fill * price / 1e18), sellers receive floor(fill * price / 1e18). The difference is recorded in dust[quoteToken].

Settlement is all-or-nothing per market. One market's failure cannot block another market of the same epoch. Inside a market, one failing transfer reverts the segment; see THREAT_MODEL.md.

The contract stores the hash of what it settled for each market in marketResult and reports it to the epoch manager, which compares it with the published hash and counts the market as settled. The epoch is SETTLED once every published market has settled. Hash formats are in MATCHING.md §9.

If a market's settlement reverts after the match, for example because a filled user cancelled their nonce onchain, revoked an allowance or cannot receive the token, the matcher re-matches that market without the order and calls amendMarket with the new hash, or withdraws the market. Other markets are unaffected. The operator that does this is in packages/operator; see packages/operator/README.md.

Funding is by ERC-20 allowance to the settlement contract. Kasumi never holds user keys. Permit-based approval is not implemented in v1.

8. Cancellation#

Private cancellation. While the epoch is open, the client sends { epochId, orderCommitment, cancelSecret } to the relay. The relay cannot check the secret, because cancelHash is inside the ciphertext, so it only records it. When the epoch is opened, the order is rejected as CANCELLED if keccak256(cancelSecret) equals the sealed cancelHash. This reveals nothing about which wallet placed the order. It depends on the relay and matcher honouring it.

Emergency onchain cancellation. KasumiSettlement.invalidateNonce(nonce) or invalidateNonces(word, mask). Works at any time and needs no cooperation from Kasumi. It reveals that the wallet uses Kasumi. Security takes priority over privacy here.