Contracts reference

Three contracts and one library, in contracts/src. Solidity 0.8.28, compiled for the paris EVM version. They are deployed on Robinhood Chain mainnet and have not had a third-party audit.

Matcher
settleMarket
KasumiSettlement
  • status, commitmentOf from the epoch manager
  • getPrice from the oracle
  • transferFrom, transfer on the two tokens
  • markMarketSettled to the epoch manager
KasumiEpochManagerroot, published hashes, state
KasumiChainlinkOracleprice, or 0 for "do not trade"
Calls between the contracts during settlement. The settlement contract reads the commitment and the oracle, moves tokens, then reports its computed result hash; the epoch manager accepts it only if it equals the published one.
Contract Mainnet address
KasumiEpochManager 0xc18aeb9ed90549b9995754aff56b7195f85848e7
KasumiSettlement 0x24e687f7e0be7bc4c4afcd65dd7a22dc87f3f399
KasumiChainlinkOracle 0x97c422c167da9ac46ee953dab2f171a9ad767e59

The source of truth for addresses is deployments/robinhood-mainnet.json. ABIs as TypeScript are in packages/operator/src/abi.ts, generated from the build output.

KasumiEpochManager#

Epoch schedule, batch commitments and the epoch state machine. Time-driven states are derived from the schedule, so an empty epoch costs no gas.

Lifecycle functions#

Function Caller Effect
commit(uint64 epochId, bytes32 root, uint32 orderCount) relay Anchors the batch. Only while the epoch is CLOSED, which means between the cutoff and the decryption time. Cannot be replaced.
triggerDecryption(uint64 epochId) anyone Emits DecryptionTriggered once, when the epoch is DECRYPTABLE or MATCHED. Nothing in v1 consumes the event.
postMatch(uint64 epochId, bytes32[] marketIds, bytes32[] marketHashes) matcher Publishes one result hash per market, ids strictly ascending. Only while DECRYPTABLE. An empty result settles the epoch at once.
amendMarket(uint64 epochId, bytes32 marketId, bytes32 newHash) matcher Replaces the published hash of a market that has not settled, or withdraws it with a zero hash. Only while MATCHED.
markMarketSettled(uint64 epochId, bytes32 marketId, bytes32 marketHash) settlement contract Reverts unless marketHash equals the published one. Moves the epoch to SETTLED when every published market has settled.
cancelEpoch(uint64 epochId) owner Emergency stop. Cannot touch a settled epoch.

Views#

Function Returns
status(uint64 epochId) SCHEDULED, OPEN, CLOSED, COMMITTED, DECRYPTABLE, MATCHED, SETTLED or CANCELLED (0 to 7)
epochTimes(uint64 epochId) (openTime, closeTime, decryptTime, settleDeadline)
currentEpochId() the epoch accepting orders, 0 before the first
getEpoch(uint64 epochId) (root, resultHash, relay, orderCount, marketCount, settledCount, matched, decryptionTriggered, settled, cancelled)
rootOf(uint64 epochId), commitmentOf(uint64 epochId) the root, and (root, orderCount)
publishedMarketHash(epochId, marketId), marketSettled(epochId, marketId) per-market result state
scheduleCount(), scheduleAt(index) schedule history
owner(), pendingOwner(), matcher(), settlement(), isRelay(address) roles

Administration#

setRelay(address, bool), setMatcher(address), setSettlement(address), reconfigure(epochDuration, revealDelay, settleWindow) (takes effect two epochs ahead, so no open or closing epoch changes), transferOwnership(address) then acceptOwnership().

Events#

EpochCommitted(epochId, root, orderCount, closeTime, decryptTime, relay), DecryptionTriggered(epochId, root, decryptTime), EpochMatched(epochId, resultHash, marketIds, marketHashes), MarketAmended(epochId, marketId, oldHash, newHash), MarketSettled(epochId, marketId, marketHash), EpochSettled(epochId, resultHash), EpochCancelled(epochId), ScheduleAdded(…), RelaySet, MatcherSet, SettlementSet, OwnershipTransferStarted, OwnershipTransferred.

Errors#

NotOwner, NotRelay, NotMatcher, NotSettlement, ZeroAddress, InvalidSchedule, SchedulePending, UnknownEpoch, WrongStatus(Status actual), EmptyCommitment, ResultHashMismatch, InvalidResult, MarketNotPublished, MarketAlreadySettled.

KasumiSettlement#

Settles one market of one epoch at one clearing price. Users approve this contract; it is the EIP-712 verifyingContract.

Settlement#

solidity
function settleMarket(uint64 epochId, bytes32 marketId, uint256 clearingPrice, Fill[] calldata fills)

struct Fill {
    KasumiOrder order;
    bytes signature;
    uint128 baseFilled;
    uint64 sequence;
    bytes32 ciphertextHash;
    bytes32[] proof;
}

Matcher only. Fills must be strictly ordered by commitment. The call is all or nothing for the market. For each fill the contract checks, from the signed order alone: tokens match the market, epoch, validity window, receiver is not zero or the contract, nonce unused (then spends it), signature (ECDSA first, then ERC-1271 with bounded gas), membership of (epochId, sequence, commitment, ciphertextHash) under the committed root with sequence < orderCount, fill size against baseAmount, minFillBase and allowPartialFill, limit price, and the order's own oracle bound. For the market it checks: enabled, epoch MATCHED, not already settled, oracle fresh, clearing price inside the market band, base in equals base out, and that the contract's balances grew by exactly what it pulled. It computes quote amounts itself: buyers pay ceil(x * P / 1e18), sellers receive floor(x * P / 1e18). Finally it calls markMarketSettled, which reverts the whole settlement unless the result equals the published hash.

User functions#

Function Effect
invalidateNonce(uint256 nonce) Emergency onchain cancellation. Works at any time.
invalidateNonces(uint256 word, uint256 mask) Cancels up to 256 nonces of one bitmap word.

Views#

Function Returns
orderDigest(KasumiOrder) EIP-712 digest
orderCommitment(KasumiOrder) keccak256(digest ‖ salt)
domainSeparator() rebuilt if the chain id changes
isNonceUsed(address, uint256), nonceBitmap(address, uint256) nonce state
markets(bytes32 marketId) (baseToken, quoteToken, enabled, maxOracleDeviationBps, maxOracleAge)
marketResult(uint64 epochId, bytes32 marketId) hash of a settled market result, zero if not settled
dust(address token) rounding residue held for that token
epochManager(), oracle(), owner(), pendingOwner(), matcher(), NAME(), VERSION() configuration

Administration#

setMarket(baseToken, quoteToken, enabled, maxOracleDeviationBps, maxOracleAge), setMatcher(address), setOracle(address), sweepDust(token, to) (only the accounted residue), transferOwnership(address) then acceptOwnership().

Events#

OrderFilled(commitment, owner, epochId, marketId, side, baseFilled, quoteAmount), MarketSettled(epochId, marketId, clearingPrice, matchedBase, oraclePrice, resultHash), NonceInvalidated(owner, word, mask), MarketSet(…), DustSwept(token, to, amount), MatcherSet, OracleSet, OwnershipTransferStarted, OwnershipTransferred.

Errors#

Group Errors
Access and state NotOwner, NotMatcher, ZeroAddress, Reentrancy, MarketDisabled, MarketAlreadySettled, EpochNotMatched(Status), EmptyBatch
Oracle OracleStale, OracleDeviation, OrderOracleDeviation
Order WrongMarket, WrongEpoch, WrongSide, NotYetValid, Expired, NonceUsed, BadSignature, NotCommitted, InvalidReceiver
Fill ZeroFill, Overfill, BelowMinFill, PartialFillNotAllowed, LimitPriceViolated, FillsNotSorted, UnbalancedBatch
Tokens TransferFailed, UnexpectedBalance

A blocklisted party or a paused Stock Token surfaces as TransferFailed.

KasumiChainlinkOracle#

Turns two Chainlink USD feeds into the protocol price. Fails closed: every problem returns price 0, which the settlement contract treats as "do not trade".

Function Notes
getPrice(bytes32 marketId) returns (uint256 price, uint256 updatedAt) Price 0 when the market is unknown, an answer is not positive, either feed is older than its own limit, the sequencer feed reports down or inside the grace period, a token has no code, or a flagged Stock Token's oraclePaused() is true or unreadable. updatedAt is the base feed's round time.
setFeeds(baseToken, quoteToken, baseFeed, quoteFeed, baseMaxAge, quoteMaxAge, baseIsStockToken, quoteIsStockToken) Owner.
removeFeeds(bytes32 marketId) Owner.
sequencerUptimeFeed(), sequencerGracePeriod() Immutable. The mainnet deployment has no sequencer feed configured.
feeds(bytes32 marketId) Stored configuration.

Events: FeedsSet(marketId, baseFeed, quoteFeed, baseMaxAge, quoteMaxAge), FeedsRemoved(marketId), ownership events. Errors: NotOwner, ZeroAddress, InvalidConfig.

Any contract implementing IKasumiOracle.getPrice can replace it through setOracle.

KasumiOrderLib#

The order struct, its EIP-712 type hash, and the hashing helpers the SDK mirrors: structHash, commitment(digest, salt), marketId(base, quote), leaf(epochId, sequence, commitment, ciphertextHash) and verifyProof(proof, root, leaf). The struct and its units are described under Order format.

Tests#

contracts/test holds 89 Foundry tests, including fuzz tests for the nonce bitmap, signed bounds under rounding, epoch contiguity and fills against signed amounts, and CrossCheck.t.sol, which replays vectors produced by the SDK through a real settlement.