DEV Community

Suliman Mokhtar
Suliman Mokhtar

Posted on Originally published at xroot.dev

Robinhood Chain: Three Things the Docs Don't Say

Originally published on xroot.dev.

I spent a day reading Robinhood Chain with eth_call instead of reading about it. Three things turned up that no documentation page mentions — and one of them is a mistake I made first.

Robinhood Chain went live on 1 July 2026: an Arbitrum Orbit L2 settling to Ethereum, ETH for gas, ~0.02 gwei, and — unusually for a chain run by a regulated US brokerage — genuinely permissionless deployment. That much the docs say, and all of it checks out.

What follows is the part that only shows up if you query the chain yourself. Each finding is also a lesson about a different way of trusting the wrong artefact: a marketing page, an ABI, and a getter. The through-line is the same one that runs through parsing raw AMM accounts instead of using an SDK: prefer evidence the subject cannot author.


Establishing Ground Truth in Three Calls

Before trusting anything about a chain, ask the chain. Chain IDs get transcribed wrong, RPC URLs get stale, and "mainnet is live" is a claim with at least four distinct meanings.

RPC=https://rpc.mainnet.chain.robinhood.com

# Chain ID: 0x1237 = 4663
curl -s -X POST $RPC -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'

# Is this really a Nitro chain? Arbitrum precompiles answer 0xfe.
curl -s -X POST $RPC -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getCode",
       "params":["0x000000000000000000000000000000000000006b","latest"]}'
Enter fullscreen mode Exit fullscreen mode

An Arbitrum Nitro chain exposes its precompiles as accounts whose code is the single byte 0xfe. That one read is worth more than any "built on Arbitrum" badge: it is a property the chain cannot fake without actually being one.

The same trick answers the question that decides whether a third party can build anything at all. Rather than trusting "permissionless", estimate gas for a contract creation from an address the chain has never seen. A deployer allowlist rejects it. This one quotes a price.

Chain ID4663 (0x1237)

StackArbitrum Orbit / Nitro → Ethereum

Gas tokenETH · ~0.023 gwei

DeploymentPermissionless — no allowlist

CREATE2's deterministic-deployment proxy, Multicall3, Permit2 and Safe v1.4.1 are all present at their canonical addresses. As EVM chains go, this one arrived furnished.


The Chain Owner's Precompiles Are Public — and One Has Been Busy

Arbitrum chains expose their own governance through ArbOwnerPublic at 0x…006b. It is readable by anyone. Two calls tell you who controls the chain:

# getAllChainOwners()          -> selector 0x516b4e0f
# getAllTransactionFilterers() -> selector 0x595fbb5a
# (derive both with keccak256(signature)[0..4] — do not trust a table)

getAllChainOwners()          -> [ 0x2a153c6a…005C09 ]   # an UpgradeExecutor proxy
getAllTransactionFilterers() -> [ 0xebDc18A1…24b7   ]   # ← one authorised filterer
Enter fullscreen mode Exit fullscreen mode

That second address is worth understanding. ArbOS added protocol-level transaction screening for Orbit chains: an authorised filterer registers a transaction hash, and from then on the state transition function forcibly fails it — including a transaction force-included through L1, which is normally the escape hatch that makes a rollup censorship-resistant.

Most write-ups stop at "the capability exists". But whether a capability has been used is a question an EOA answers for free, because its nonce is public and its history is indexed:

eth_getTransactionCount(0xebDc18A1…24b7) -> 0x17cc   # = 6,092 transactions

# What are they? Page the explorer's txlist and look at the destination + selector.
to:       0x0000000000000000000000000000000000000074   # the filter precompile
selector: 0xcb470491                                    # addFilteredTransaction(bytes32)
status:   1                                             # every one succeeded
Enter fullscreen mode Exit fullscreen mode

6,092 transactions, and the ones I sampled are all filter calls. The first landed 30 June 2026 at 14:53 UTC — the day before the public mainnet launch. The most recent was 9 August. That is roughly 150 filtered transactions a day across the chain's first six weeks, with no published criteria, no volume disclosure, and no appeals process I could find.

I want to be precise about what this does and does not establish. It shows the mechanism is operational rather than dormant. It does not tell you what was filtered or why — the hashes reveal nothing about intent — and the filterer EOA carries no name or public tag, so attributing it to Robinhood is inference, not proof. The question I actually care about, and could not answer from the blocklist alone, is whether a contract deployment has ever been filtered.

If you are considering deploying something with a revenue stream on this chain, that is the risk to price: not a lawsuit, but a switch.


An ABI Is Not a Contract — I Got This Wrong First

Robinhood's tokenized stocks are ordinary ERC-20s. Whether they restrict transfers has been publicly disputed, so I went to settle it. I pulled the implementation behind the proxy, read its ABI, and searched the function list for the usual suspects — canTransfer, isWhitelisted, an identity registry, anything ERC-3643 shaped. Nothing. I concluded transfers were unrestricted apart from a global pause.

That conclusion was wrong, and the reason is a detail worth carrying around:

A Solidity modifier is inlined into the functions it guards. It never appears as its own ABI entry. An ABI enumerates what you can call. It says nothing about what happens on the way in. You cannot prove the absence of a restriction from an ABI — only from source or bytecode.

The verified source — 191,692 characters of it — settles the question immediately:

modifier onlyNotBlocked(address account) {
    if (IAccessControlsRegistry(ACCESS_CONTROLLED_REGISTRY).isBlocked(account)) {
        revert Blocked(account);
    }
    _;
}

function transfer(address to, uint256 value) public override
    onlyNotPaused
    onlyNotBlocked(to)
    onlyNotBlocked(_msgSender())
    returns (bool)

function transferFrom(address from, address to, uint256 value) public override
    onlyNotPaused
    onlyNotBlocked(from)
    onlyNotBlocked(to)
    onlyNotBlocked(_msgSender())
    returns (bool)
Enter fullscreen mode Exit fullscreen mode

onlyNotBlocked appears fifteen times. canTransfer, whitelist and allowlist appear zero times. So both halves of my original answer needed splitting apart:

  • No allowlist. You do not need permission to receive one. That part was right, and it is why these tokens compose with ordinary DeFi at all.
  • But a per-address blocklist, checked on both sides of every transfer and on the caller. This is the USDC/USDT model — default-open, revocable — not ERC-3643's default-closed model.

That distinction is the whole commercial question. Default-open means a wallet, a portfolio tracker or an AMM works normally. Default-closed would mean none of them do. It is a good outcome — it is simply not the one the documentation states, because the documentation does not mention the blocklist at all.


Identity: Ask for Evidence the Subject Cannot Author

Search the chain's explorer for TSLA and you get fifty results. One is the real tokenized Tesla. Among the rest is a token that copies the genuine name character for character, and a cluster whose addresses all end in the same few characters — the signature of a script, not an issuer.

So how do you identify the real one? There is a getter that looks perfect for it: ACCESS_CONTROLLED_REGISTRY(), which on a genuine stock token returns the registry address. Call it on the impostor and it reverts. Job done?

No — because a getter is code, and the thing you are interrogating wrote it. Any contract can implement that function to return whatever value makes it look legitimate. It happens to work here only because these particular fakes did not bother.

The stronger check reads storage the contract cannot lie about. These tokens are beacon proxies, so the beacon address lives at the fixed EIP-1967 slot — and eth_getStorageAt bypasses contract code entirely:

SLOT=0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50  # EIP-1967 beacon

eth_getStorageAt(TSLA, SLOT) -> 0x…e10b6f6b275de231345c20d14ab812db62151b00
eth_getStorageAt(AAPL, SLOT) -> 0x…e10b6f6b275de231345c20d14ab812db62151b00
eth_getStorageAt(NVDA, SLOT) -> 0x…e10b6f6b275de231345c20d14ab812db62151b00
eth_getStorageAt(SPY,  SLOT) -> 0x…e10b6f6b275de231345c20d14ab812db62151b00

eth_getStorageAt(the impostor, SLOT) -> 0x0000…0000
Enter fullscreen mode Exit fullscreen mode

Four genuine tokens, one beacon, one factory. The impostor returns nothing, because it is not a beacon proxy and has no such slot to populate. A storage read is not an opinion and cannot be forged by the contract being read.

Never match on name or symbol. Memecoins on this chain have started appending the exact • Robinhood Token suffix to their own names — there is one called "Hoodrat • Robinhood Token". Any pipeline that discovers assets by string match has an impersonation vector, not a check.


One Address Holds Identity, Transferability and the Kill Switch

Follow the three findings and they converge on the same contract. The beacon that proves a token is genuine is the same registry the blocklist is read from — and the same registry that can halt every stock token at once:

function paused() public view returns (bool) {
    StockStorage storage $ = _getStockStorage();
    return $.paused || IAccessControlsRegistry(ACCESS_CONTROLLED_REGISTRY).paused();
}
Enter fullscreen mode Exit fullscreen mode

Read that disjunction carefully. A stock token is paused if its own flag is set or if the registry's global flag is set. One transaction against 0xe10b…1b00 freezes the entire tokenized equity market on this chain. The same registry answers isBlocked, so an address blocked once is blocked on every stock token simultaneously.

Both flags read false today, and the multiplier that adjusts share counts for splits and dividends sits at exactly 1e18. Nothing is being exercised. That is rather the point: this is what the healthy state looks like, and the capabilities are invisible from the outside unless you go and read them.

None of this is scandalous. A regulated tokenized security needs a pause for corporate actions and a blocklist for sanctions compliance; an instrument that could not do those things could not legally represent a stock. The observation is narrower and, I think, more useful: on an ordinary token these powers are a red flag, and on a regulated one they are a requirement — so any generic token scanner pointed at a tokenized stock will produce a confidently wrong verdict.


What I Would Take Away

  • Probe with a control. Every "is X deployed" sweep should include an address you know is empty. If your method cannot produce a negative, it is not measuring anything.
  • Derive selectors, do not look them up. Four bytes of keccak is cheaper than trusting a table, and it catches the case where the function you think you are calling does not exist.
  • Never infer absence from an ABI. Modifiers are inlined. So are hooks. Presence is provable from an ABI; absence is not.
  • Prefer storage over getters, and nonces over announcements. Rank your evidence by how hard it would be for the subject to fabricate.
  • Capability is not usage — but usage is usually measurable. The gap between "a filter exists" and "it has run 6,092 times" was one nonce read away.

Robinhood Chain is open, well-provisioned and easy to build on, and I would take everything above as an argument for reading carefully rather than an argument against the chain. It is the same habit that made the economics of Solana's rent cut read so differently from the headlines about it. The uncomfortable finding is not that a brokerage put controls on its own tokenized securities — it is how much of that only exists in storage slots and nonces, and how little of it exists in prose.

— Check a Token Before You Trust It —

Fifty results for one ticker. One of them is real.

I build free, read-only token reports that do these checks for you — identity from storage rather than from a name, and an honest "unknown" where the chain cannot answer. No wallet connection, no signature.

Run a Token Report ↗Lock Down a Token

Everything here is reproducible against https://rpc.mainnet.chain.robinhood.com and the chain's Blockscout instance. Reference: Robinhood Chain docs, Arbitrum on transaction filtering, and L2BEAT's risk breakdown. Figures read on 25 August 2026; re-run them before relying on any of it.

xroot.dev is not affiliated with, endorsed by, or sponsored by Robinhood Markets, Inc. "Robinhood Chain" is used here only to name the public blockchain this article examines. Nothing here is financial advice.

Top comments (0)