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.
settleMarketstatus,commitmentOffrom the epoch managergetPricefrom the oracletransferFrom,transferon the two tokensmarkMarketSettledto the epoch manager
| 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#
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.