Robinhood Chain is an Arbitrum Orbit L2 settling to Ethereum, using ETH for gas, chain id 4663 on mainnet and 46630 on testnet. It is not Unichain, which we confused it with once and fixed in commit 977c316. Most of building on it is ordinary EVM work. The rest is below.
The public RPC is not for backfills
Robinhood’s public endpoint rate limits aggressively. A few hundred calls in quick succession and it answers 429 to everything — eth_blockNumber included — for a while afterwards. An indexer without pacing wedges itself on the first sweep and then looks like a crash.
Every RPC call in the indexer goes through paced(): a minimum interval between calls (default 250 ms; ~1500 ms on the public endpoint), exponential backoff on 429 up to 30 s, and a retry budget of eight. eth_getLogs is chunked at 4 million blocks, which sounds enormous until you learn the chain mints about 864,000 blocks a day, so a full-history sweep is a handful of calls. And snapshots are refreshed before history is backfilled, so markets get real prices before the swap backfill finishes. None of that replaces a dedicated endpoint; it just means the indexer degrades instead of dying.
viaIR is not optional
LiquorLaunchFactory.launchToken juggles a TokenParams struct with nested socials, a DexConfig, a LaunchConfig, and a MintParams. Without viaIR: true the legacy code generator runs out of stack slots and the factory does not compile at all. This is a build setting that is load-bearing, and the Hardhat config says so in a comment so nobody “cleans it up”.
600 bytes
With evmVersion: paris the factory compiled to roughly 600 bytes over the 24,576-byte deployment limit. Shanghai adds PUSH0, which shaves a byte off every zero push, and that closes the gap. We stopped there rather than going to Cancun because TSTORE and MCOPY support varies across Orbit chains and nothing in the contracts needs them.
A pleasant side effect: the locker compiles to 5,426 bytes, exactly the size of pons’s deployed locker. That is not proof the build reproduces theirs, but it is the kind of coincidence that does not happen by accident.
There is no testnet, for our purposes
Robinhood Chain testnet exists and has a faucet. Uniswap V3 is not deployed there — the factory, position manager, router and WETH addresses all return empty bytecode, checked 7 September 2026. Our factory cannot create a pool without them, so it cannot run on testnet at all. The deploy script refuses any chain but 4663 and verifies each dependency address holds code, because the alternative failure — deploying fine and reverting on someone’s first launch — is worse.
What we do instead is fork mainnet. FORK=1 npm run contracts:test runs ten cases against the real Uniswap V3 contracts at a pinned block. It is a good substitute for a testnet. It is not a substitute for the first real launch, which will be done with a trivial amount and reconciled against the indexer before anything else.
Wallet balances on the right chain
A small one that bit us: the balance hooks read from DEFAULT_CHAIN, which defaulted to testnet, while the trades next to them settled on mainnet. useEthBalance and useTokenBalance now pin 4663 directly. If your app has a “default chain” and a “chain writes actually go to”, make sure they are the same variable.