Deployment and operations

Where the contracts are deployed, how the relay is configured in each mode, the runbook that was followed for mainnet, and what operating it involves.

owner
0xB5D4634a3951aea316EbeBb048D99160feCBd7Ae
relay
0xc12a6B154057B34D93f0452Fb691037B2C09a8bd
matcher
0xffcd13AA8C59d0e82763ceF2619c7d78b946c996
Read at build time from deployments/robinhood-mainnet.json. Chain id 4663, deployed 2026-10-02. Unaudited.

Mainnet deployment#

The relay anchors each batch root with KasumiEpochManager.commit and the matcher settles through KasumiSettlement. Balances and allowances are read from the chain.

The contracts were deployed to Robinhood Chain mainnet on 2026-10-02. They are unaudited.

Contract Address
KasumiEpochManager 0xc18aeb9ed90549b9995754aff56b7195f85848e7
KasumiSettlement 0x24e687f7e0be7bc4c4afcd65dd7a22dc87f3f399
KasumiChainlinkOracle 0x97c422c167da9ac46ee953dab2f171a9ad767e59

Roles, schedule and market parameters are recorded in deployments/robinhood-mainnet.json (explained in deployments/README.md).

What has been verified on mainnet: the wiring and roles read back correctly, and one epoch (epoch 6) ran through the deployed contracts with a single order from an unfunded wallet. The relay key committed the batch (0x8cddb439d7f5dd5f781115c97ad21fa87330d457c61a4d473b8ed861455728ac), the order was rejected for insufficient funds, and the matcher key published the empty result (0xf602b20d6aef82b92a8066f3beeaed19222a3621ec8c78f96a3fbc77b9349d55), which settled the epoch. No trade with real tokens has settled on mainnet yet.

Robinhood Chain parameters#

Mainnet Testnet
Chain id 4663 46630
Public RPC (rate-limited) https://rpc.mainnet.chain.robinhood.com https://rpc.testnet.chain.robinhood.com
Explorer https://robinhoodchain.blockscout.com https://explorer.testnet.chain.robinhood.com
Gas token ETH ETH

Robinhood Chain is an Arbitrum Orbit chain. block.number returns an L1 block estimate; the contracts use block.timestamp only. No Stock Token addresses were found for the testnet, so a testnet deployment needs mock tokens and a mock oracle.

Contracts#

text
cd contracts
forge build
forge test

Deploy#

script/Deploy.s.sol deploys KasumiEpochManager, KasumiChainlinkOracle and KasumiSettlement and wires the relay, matcher and settlement roles. The account that broadcasts is the owner of all three.

Variable Required Default Meaning
KASUMI_RELAY yes address allowed to call commit
KASUMI_MATCHER yes address allowed to call postMatch, amendMarket and settleMarket
KASUMI_OWNER no the broadcaster a different final owner. The script then starts a two-step transfer on all three contracts and that owner must call acceptOwnership() on each.
KASUMI_EPOCH_DURATION no 30 seconds
KASUMI_REVEAL_DELAY no 12 seconds between cutoff and decryption time
KASUMI_SETTLE_WINDOW no 120 seconds after decryption time to settle
KASUMI_START_TIME no now + 300 unix time the first epoch opens
KASUMI_SEQUENCER_FEED no none Chainlink L2 sequencer uptime feed for the oracle adapter. Unset disables the check.
KASUMI_SEQUENCER_GRACE no 3600 seconds after a sequencer restart during which no price is served
text
forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY

The sequencer uptime feed and grace period are constructor arguments of the oracle adapter and cannot be changed later. Whether Chainlink publishes a sequencer uptime feed on Robinhood Chain has not been verified. If there is none, leave the variable unset: the adapter then skips the check, and prices that were fresh before a sequencer outage are accepted as soon as it restarts, within the feed age limits.

The reveal delay must leave enough time for the commit transaction to land before the decryption time. If it does not land, the epoch cancels and its orders are revealed without executing (see THREAT_MODEL.md). The commit deadline is judged by the sequencer's block.timestamp while the key is released on wall-clock time, so the delay also has to absorb any lag between the two.

List a market#

The deploy script lists no markets. Listing is a separate, deliberate step. Only list a token after verifying its contract and its transfer behaviour.

Verification procedure for each token and feed:

  1. Take the token address from the issuer's registry (for Stock Tokens, https://api.robinhood.com/rhj/assets) and the feed address from Chainlink's feed directory.
  2. Read back onchain: symbol() and decimals() on the token, description() and decimals() on the feed, and one latestRoundData(). They must match what you expect to list.
  3. Check that the feed prices the same unit the order is signed in. Chainlink's Stock Token feeds price one raw token (equity price times uiMultiplier), which is what Kasumi uses.
  4. Check transfer behaviour: no fee on transfer, transferFrom between two user wallets through a third party works. The settlement contract reverts if the amount received differs from the amount pulled.

The mainnet addresses in apps/web/src/server/config.ts (AAPL, NVDA, TSLA, SPY, USDG and their feeds) were read back this way on 2026-10-02. Re-verify before listing; Stock Tokens are upgradeable.

script/ListMarkets.s.sol does the listing for the four mainnet Stock Token markets against USDG. It refuses to run on any chain but 4663. Before it sends anything it reads every token and feed back from the chain and reverts on a mismatch: symbol(), decimals() (18 for Stock Tokens, 6 for USDG), feed decimals() (8), a positive feed answer, and a readable oraclePaused() that is false. For each market it then calls KasumiChainlinkOracle.setFeeds(base, USDG, baseFeed, usdgFeed, baseMaxAge, quoteMaxAge, true, false) and KasumiSettlement.setMarket(base, USDG, true, maxDevBps, maxOracleAge), and finally requires a non-zero price from the adapter.

Variable Default Meaning
KASUMI_SETTLEMENT required settlement contract; the oracle adapter is read from it
KASUMI_MAX_DEVIATION_BPS 300 market-wide bound on the clearing price against the oracle
KASUMI_BASE_MAX_AGE 93600 seconds, limit on the equity feed round inside the adapter
KASUMI_QUOTE_MAX_AGE 93600 seconds, limit on the USDG/USD round inside the adapter
KASUMI_MAX_ORACLE_AGE 93600 seconds, the settlement contract's own limit on the base round
KASUMI_REQUIRE_FRESH true fail if the adapter returns no price right after listing. Set false to list while the equity market is closed.

The Chainlink aggregators use opcodes newer than this repository's compile target (paris), and forge simulates a script under the compile target's rules, so this one script has to be simulated as cancun. Give it its own output and cache directories so the paris artifacts in contracts/out are not replaced:

text
FOUNDRY_EVM_VERSION=cancun FOUNDRY_OUT=/tmp/kasumi-list/out FOUNDRY_CACHE_PATH=/tmp/kasumi-list/cache \
KASUMI_SETTLEMENT=$SETTLEMENT \
forge script script/ListMarkets.s.sol --rpc-url $RPC_URL --broadcast --private-key $OWNER_PRIVATE_KEY

Why 26 hours for the ages. Observed onchain on 2026-10-02: the USDG/USD feed updates once a day, weekends included (86,401 to 86,427 seconds between the last seven rounds). The equity feeds update on a price move while the US market is open and stop when it closes (gaps between the last seven AAPL rounds ran from 20 minutes to 16 hours on weekdays). A base limit of minutes would halt a quiet market for most of the day. With 26 hours every market still halts over a weekend. The price of this choice is that a settlement can use an equity price up to a day old; the market-wide deviation bound and each order's own limit are what protect a trader in that case.

maxOracleDeviationBps must be between 1 and 9999 and maxOracleAge must be non-zero. The adapter returns no price if either feed is older than its own limit; maxOracleAge in setMarket is then applied to the base feed's timestamp. Both tokens must have code. Flag a token as a Stock Token only if it implements oraclePaused(): the Stock Tokens on mainnet answer it, USDG does not, so USDG is not flagged. removeFeeds(marketId) takes a market's price away, which stops it settling. To list any other pair, call setFeeds and setMarket directly with the same arguments.

Users fund by approving the settlement contract for the token they spend.

Web app and relay on Vercel#

The public address is https://kasumisystems.com. The Vercel alias kasumi-zeta.vercel.app serves the same deployment and is the fallback if the domain is unavailable. Site metadata, robots.txt and sitemap.xml use the domain as the canonical origin.

The Next.js app in apps/web serves the terminal, the docs and the relay API. Set the Vercel project's root directory to apps/web; the build needs the rest of the repository (the SDK workspace package and docs/), which Vercel includes by default.

Relay and site:

Variable Default Meaning
KASUMI_MODE unset must be live for a deployed relay: it turns on onchain commit and settlement.
KASUMI_RELAY_KEY required private key that signs inclusion receipts and sends commit. Its address must be allowed by setRelay.
DATABASE_URL unset Postgres connection string (Neon, HTTP driver). Used when no Redis is configured. The relay creates its own kasumi_* tables on first use.
KV_REST_API_URL, KV_REST_API_TOKEN unset Upstash Redis REST endpoint and token. UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN are also read.
ROBINHOOD_CHAIN_RPC_URL public mainnet RPC RPC used for reference prices and for every chain read and transaction. The public RPC is rate-limited; use a dedicated endpoint for a live relay.
ROBINHOOD_CHAIN_EXPLORER_URL https://robinhoodchain.blockscout.com explorer base URL given to clients for transaction links
NEXT_PUBLIC_PRIVY_APP_ID the project's Privy app id Privy application used for wallet login in the terminal. A Privy app id is public. The deployment's origin must be allowed in the Privy dashboard.
NEXT_PUBLIC_X_URL https://x.com/ link behind the X button in the footer

Chain and operator:

Variable Default Meaning
KASUMI_EPOCH_MANAGER required KasumiEpochManager address. The epoch schedule is read from it.
KASUMI_SETTLEMENT required KasumiSettlement address. It becomes the EIP-712 verifyingContract.
KASUMI_MATCHER_KEY required for the inline operator private key that sends postMatch, amendMarket and settleMarket. Its address must be set with setMatcher on both contracts.
KASUMI_OPERATOR inline inline: this server sends the commit and settlement transactions. external: it does not, and a standalone operator process must.
KASUMI_OPERATOR_TOKEN unset bearer token for GET /api/v1/operator/epochs/:id. Without it that endpoint always answers 401.
CRON_SECRET unset if set, GET /api/v1/cron requires Authorization: Bearer <secret>. Vercel Cron sends it automatically.

Set keys and tokens as sensitive variables (vercel env add <NAME> production --sensitive). Environment changes take effect on the next deployment.

Storage is chosen in this order: Redis if configured, else Postgres if DATABASE_URL is set, else process memory. On a serverless host each instance has its own memory, so a hosted relay needs Redis or Postgres. The hosted relay uses a Neon Postgres database provisioned through the Vercel Marketplace (vercel integration add neon). All relay endpoints are served by one route handler (apps/web/src/app/api/v1/[...path]/route.ts). Every storage key is prefixed with the settlement address, so two deployments sharing one database cannot read each other's state.

The epoch schedule comes from the epoch manager and is re-read every five minutes.

Running the relay locally without chain access is described in docs/DEVELOPMENT.md in the repository.

Operating the live relay#

With KASUMI_MODE=live and KASUMI_OPERATOR=inline (the default) the web relay is also the operator. It uses packages/operator (documented in packages/operator/README.md) to send the transactions.

Per epoch that has at least one order:

  1. After the cutoff and before the decryption time, the relay key sends commit(epochId, root, orderCount).
  2. After the drand round for the decryption time is published, the matcher key sends postMatch with one hash per market that trades, then one settleMarket per market. The epoch becomes SETTLED onchain when every published market has settled. An epoch in which nothing crosses settles with an empty result.

Epochs without orders cost nothing and send nothing.

Three things drive those steps. Each one calls the same idempotent function, and a store lock keeps the two keys from sending concurrent transactions:

  • The instance that accepted the epoch's first order. It stays alive after responding (after()), sleeps until the cutoff, commits, sleeps until the drand round, and settles. The route's maxDuration is 300 seconds for this reason.
  • Any client polling GET /api/v1/state. The terminal polls every two seconds.
  • A cron job. apps/web/vercel.json schedules GET /api/v1/cron every minute. Set CRON_SECRET so only the scheduler can call it.

What can go wrong, and what happens:

  • The commit misses its window. The window is the reveal delay (15 seconds on mainnet). If no instance, poller or cron run is alive inside it, or the RPC rejects the transaction, the contract refuses a late commit. The epoch cancels and its orders become public without executing. Nothing can be traded from them, but their intent is revealed. The cron job alone is not enough to prevent this: it runs once a minute.
  • Settlement is interrupted. settleEpoch resumes from chain state, so the next poll or cron run continues. If the settlement window (300 seconds on mainnet) passes first, markets that already settled stay settled and the rest of the epoch cancels.
  • A market's settlement reverts. The operator re-matches that market and amends the published result; see packages/operator/README.md. A blocklisted party or a paused Stock Token makes transfers revert. The operator reads the issuer's registry before matching: orders whose owner or receiver is blocked are rejected as TRANSFER_BLOCKED, and a paused market is halted for the epoch instead of being published.
  • A key runs out of gas. Commits or settlements fail and epochs cancel. Watch the relay and matcher balances. Rough costs are in the next section.
  • The RPC rate-limits. The public RPC returned Cloudflare 403 responses to one client on this project after heavy use. A live relay should use a dedicated RPC endpoint.

The relay and matcher private keys are held as environment variables on the host. Anyone who can read the project's environment can censor, cancel epochs by not committing, or pick results within the limits in THREAT_MODEL.md. They cannot move user funds outside a valid signed order.

To run the operator as a separate process instead, set KASUMI_OPERATOR=external and KASUMI_OPERATOR_TOKEN on the relay, and start packages/operator with the same token (see its README). Do not run both against the same keys.

Checking a live relay#

apps/web/scripts/live-e2e.mts deploys the contracts with mock tokens to a local anvil chain and drives the server's own functions through one epoch: three orders, a private cancellation, commit, drand decryption, settlement, and balance checks. It needs anvil, forge build output in contracts/out, and network access to drand.

text
cd contracts && forge build && cd ..
pnpm --filter @kasumi/web live-e2e

apps/web/scripts/mainnet-smoke.mts runs one real epoch on mainnet with an order from an empty throwaway wallet. The order is rejected for insufficient funds, so nothing trades, but the relay key sends a real commit and the matcher key a real postMatch. It reads the addresses from deployments/robinhood-mainnet.json and costs a little gas.

text
cd apps/web
KASUMI_RELAY_KEY=0x... KASUMI_MATCHER_KEY=0x... npx tsx scripts/mainnet-smoke.mts

Do not run it while the hosted relay is live with the same keys: two operators on one key race on nonces.

Mainnet deployment, as performed#

This is the procedure that was run on 2026-10-02. It was first rehearsed end to end on a local fork of Robinhood Chain mainnet with pnpm --filter @kasumi/operator rehearse (see packages/operator/README.md); the gas figures below are from that rehearsal. The values actually used are noted at each step.

Three keys: a deployer that is also the owner, a relay key and a matcher key. Keep them outside the repository. The commands read them from the environment.

  1. Fund the deployer on Robinhood Chain (chain id 4663).

    Account What it pays for Gas ETH at 0.031 gwei Suggested funding
    deployer / owner three deployments, four wiring calls, listing four markets 6,994,052 0.00022 0.002
    relay one commit per epoch that has orders 82,021 0.0000026 0.003 (about 1,000 epochs with headroom)
    matcher postMatch plus one settleMarket per market that trades 134,025 + about 412,000 for a 2-fill market, about 128,000 more per extra fill 0.000034 for the rehearsed epoch (two markets, five fills) 0.005 (about 150 such epochs at the current price, with 5x headroom for gas price moves)

    Robinhood Chain charged no L1 data fee when this was measured: gasUsedForL1 was 0 on recent mainnet receipts and NodeInterface.gasEstimateL1Component returned 0. If that changes, real costs rise with calldata size (a settleMarket with three fills carries about 3.1 kB). Epochs without orders cost nothing. A user's approve costs about 58,000 gas for USDG and 64,000 for a Stock Token.

  2. Deploy. The broadcaster becomes the owner.

    text
    cd contracts
    export RPC_URL=https://rpc.mainnet.chain.robinhood.com
    KASUMI_RELAY=0xc12a6B154057B34D93f0452Fb691037B2C09a8bd KASUMI_MATCHER=0xffcd13AA8C59d0e82763ceF2619c7d78b946c996 \
    KASUMI_EPOCH_DURATION=30 KASUMI_REVEAL_DELAY=15 KASUMI_SETTLE_WINDOW=300 \
    forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY
    

    Result: KasumiEpochManager 0xc18aeb9ed90549b9995754aff56b7195f85848e7, KasumiChainlinkOracle 0x97c422c167da9ac46ee953dab2f171a9ad767e59, KasumiSettlement 0x24e687f7e0be7bc4c4afcd65dd7a22dc87f3f399, owner 0xB5D4634a3951aea316EbeBb048D99160feCBd7Ae. The first epoch opened at unix time 1790947925. The reveal delay is 15 seconds and the settlement window 300 seconds, longer than the script defaults, because a serverless relay commits and settles more slowly than a dedicated process. No sequencer uptime feed was configured.

    Note the three addresses it prints and the time the first epoch opens (five minutes after the script runs unless KASUMI_START_TIME is set). The broadcast record is written to contracts/broadcast/Deploy.s.sol/4663/run-latest.json.

  3. List the four markets. Run this while the US equity market is open so that every feed is fresh; otherwise add KASUMI_REQUIRE_FRESH=false.

    text
    FOUNDRY_EVM_VERSION=cancun FOUNDRY_OUT=/tmp/kasumi-list/out FOUNDRY_CACHE_PATH=/tmp/kasumi-list/cache \
    KASUMI_SETTLEMENT=0x24e687f7e0be7bc4c4afcd65dd7a22dc87f3f399 \
    forge script script/ListMarkets.s.sol --rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY
    

    This was run with the defaults while the US market was open: AAPL, NVDA, TSLA and SPY against USDG, deviation bound 300 bps, all three ages 93,600 seconds.

  4. Check what is onchain before anyone trades.

    text
    cast call <epoch manager> "owner()(address)" --rpc-url $RPC_URL
    cast call <epoch manager> "isRelay(address)(bool)" <relay address> --rpc-url $RPC_URL
    cast call <epoch manager> "matcher()(address)" --rpc-url $RPC_URL
    cast call <epoch manager> "settlement()(address)" --rpc-url $RPC_URL
    cast call <settlement> "matcher()(address)" --rpc-url $RPC_URL
    cast call <settlement> "oracle()(address)" --rpc-url $RPC_URL
    

    On mainnet these returned the owner, relay, matcher and settlement addresses above, and markets(marketId) for AAPL returned the token pair, enabled, 300 and 93600.

  5. Fund the relay and matcher addresses from the deployer.

    text
    cast send <relay address> --value 0.003ether --rpc-url $RPC_URL --private-key $DEPLOYER_PRIVATE_KEY
    cast send <matcher address> --value 0.005ether --rpc-url $RPC_URL --private-key $DEPLOYER_PRIVATE_KEY
    

    On mainnet only 0.002 ETH was available in total, so the relay received 0.0005 ETH and the matcher 0.0009 ETH. That covers a few dozen epochs with orders and must be topped up for sustained use. By this point the public RPC was answering cast with a Cloudflare 403 from the deploying machine, so the two transfers were sent with a short viem script instead; plain JSON-RPC requests still worked.

  6. Point the operator at the deployment. For the hosted app, set KASUMI_MODE=live, KASUMI_EPOCH_MANAGER, KASUMI_SETTLEMENT, KASUMI_RELAY_KEY, KASUMI_MATCHER_KEY, CRON_SECRET, KASUMI_OPERATOR_TOKEN and NEXT_PUBLIC_PRIVY_APP_ID on the Vercel project (keys and tokens as sensitive variables) and redeploy; see "Web app and relay on Vercel" and "Operating the live relay". For the standalone runner instead (packages/operator/README.md): RPC_URL, EPOCH_MANAGER, SETTLEMENT, RELAY_PRIVATE_KEY, MATCHER_PRIVATE_KEY, RELAY_API_URL, OPERATOR_TOKEN and KASUMI_ALLOW_MAINNET=1.

  7. Run one epoch before announcing anything: apps/web/scripts/mainnet-smoke.mts (see "Checking a live relay"). On mainnet this was epoch 6: commit 0x8cddb439d7f5dd5f781115c97ad21fa87330d457c61a4d473b8ed861455728ac, order rejected INSUFFICIENT_FUNDS, postMatch 0xf602b20d6aef82b92a8066f3beeaed19222a3621ec8c78f96a3fbc77b9349d55, epoch status SETTLED with root 0xf5f6cc342d78cd36c66acfd4f31cbe31fc5ebb23f144230c1348aa85029cc542 and order count 1.

What the rehearsal established about the real tokens (fork of mainnet, 2026-10-02):

  • approve plus transferFrom by the settlement contract between two ordinary wallets works for the AAPL Stock Token and for USDG, and the amounts that move are exactly the raw amounts requested. No fee, no rebasing: the balance guard in settleMarket holds.
  • uiMultiplier() was 1.000566 for AAPL, 1.000775 for NVDA, 1.0 for TSLA and 1.001718 for SPY. Balances, allowances and transfers are in raw units, and the Chainlink feed prices one raw token.
  • Blocklist. With an address forced onto the registry's blocklist on the fork, a Stock Token transfer reverts (error selector 0x75e91ce7, carrying the blocked address) when the blocked address is the sender of the funds, the recipient, or msg.sender. If the settlement contract itself were blocked, no Stock Token market could settle.
  • Pause. A per-token pause() makes that token's transfers revert (selector 0x1309a563) and leaves the other Stock Tokens working. The registry's pause() stops every Stock Token at once. Both were triggered on the fork by forcing roles through storage; who holds those roles on mainnet was not established.
  • Either condition reverts that market's settleMarket. Other markets are unaffected. At the time of the rehearsal the operator's re-match only dropped orders that failed its own re-validation (nonce, balance, allowance) and did not read the blocklist, so a market with a blocked party failed its retries and was withdrawn for the epoch. The operator has since been made aware of both (see packages/operator/README.md): the extended rehearsal covers a party blocked before decryption and one blocked after postMatch, and both epochs settle.
  • ERC-2612 permit works on the AAPL Stock Token (domain name "Apple • Robinhood Token", version "1"). Kasumi v1 does not use it.
  • A full epoch settled through the real KasumiChainlinkOracle and the real feeds: five orders in two markets, cleared at the oracle price, balances moved exactly as matched, one raw USDG unit of dust.

Open items after the mainnet deployment#

The contracts were deployed to mainnet at the project owner's instruction with these items open. None of them has been done. Each is a reason to keep amounts small.

  • External audit of KasumiEpochManager, KasumiSettlement, KasumiChainlinkOracle and KasumiOrderLib.
  • Legal review. Stock Tokens are tokenised debt securities issued by a Robinhood entity and are not offered to US persons. Whether a third-party venue may match them, for whom, and under which licences must be settled first. Kasumi is not affiliated with, endorsed by or operated by Robinhood.
  • Key management. Today the owner is a single key on one machine, and the relay and matcher keys are environment variables on the web host. Move the owner behind a multisig or timelock (two-step transfer), and the relay and matcher keys into an HSM or KMS, with rotation through setRelay and setMatcher.
  • Dedicated RPC. The live relay uses the public rate-limited RPC unless ROBINHOOD_CHAIN_RPC_URL is set.
  • Gas funding. The relay and matcher accounts hold very little ETH. Top them up and alert on low balances.
  • A real trade. No trade with real tokens has settled on mainnet. Settle one small trade end to end through the terminal (wallet login, approval, order, settlement) before relying on the deployment.
  • Commit liveness. The commit depends on a serverless instance, a polling client or the cron job being alive inside the 15-second reveal window. A dedicated operator process removes that dependency.
  • Legal pages. The privacy, terms, cookies and risk pages are unreviewed draft templates with placeholders for the legal entity.
  • Oracle limits. ListMarkets.s.sol uses 26 hours for both feed ages and for maxOracleAge, because the equity feeds only update on a price move and the USDG feed once a day (see "List a market"). That lets a settlement use an equity price up to a day old. Decide whether that is acceptable per market, and the market-wide deviation bound with it. The contracts enforce the 26-hour limits.
  • Timing. Mainnet runs 30-second epochs with a 15-second reveal delay. That delay was not chosen from measured commit latency or sequencer timestamp lag. Measure both and call reconfigure if the margin is thin.
  • Sequencer uptime feed. Find out whether one exists on Robinhood Chain and deploy the oracle adapter with it if so.
  • Open review findings. Work through the open and accepted items in SECURITY_REVIEW.md.
  • Monitoring. Alert on missed commits, epochs that reach CANCELLED, settlement reverts, MarketAmended events, result hash mismatches, oracle staleness, drand beacon delay, and dust growth.
  • Independent verification. Run a second party's matcher against each public batch and compare result hashes.
  • Token behaviour. Re-verify each listed token after any issuer upgrade. The blocklist and pause behaviour is known from a fork of mainnet (see above), not from the issuer; who holds those roles was not established.
  • Rate limiting and abuse. The limits are 12 orders per IP per epoch and 120 orders per epoch in live mode. Ciphertexts are anonymous and every committed epoch costs the operator gas, so spam control needs a real design (stake, fees or allowlisted clients).