Research / Chain files / Robinhood Chain

Robinhood Chain: who can change what the oracle reads

63 targets on Robinhood Chain are tracked and scored by an oracle on the Robinhood Chain testnet: the longest tail of small protocols of any chain here, the tokenized stocks, Uniswap, Chainlink, LayerZero, Morpho and the rollup's own authority. Robinhood Chain is the chain where the oracle started, and every target was re-derived from zero in turn.

What a track judge can check in two minutes

Three calls on the Robinhood Chain oracle, read at the latest block. They need a public RPC and cast (Foundry) and nothing from us. The public RPC of this chain keeps no history for that block, so the values below are the ones the Watch read at block 136039274: they change when the oracle is re-pushed.

RPC=https://rpc.testnet.chain.robinhood.com/rpc
ORACLE=0x9BF45734D09bC7CA39238e767B2af9AAc62a7f52
TARGET=0x86923f96303d656e4aa86d9d42d1e57ad2023fdc   # Robinhood tokenized stocks/ETFs (5 of ~230 sampled)

# 1. how many contracts this oracle tracks
cast call $ORACLE "trackedTargetsCount()(uint256)" --rpc-url $RPC

# 2. its score, five sub-scores then the composite, then lastUpdated and the method hash
cast call $ORACLE "getScore(address)((uint8,uint8,uint8,uint8,uint8,uint8,uint64,bytes32))" $TARGET --rpc-url $RPC

# 3. is that score older than maxStaleness
cast call $ORACLE "isStale(address)(bool)" $TARGET --rpc-url $RPC

Expected

1 -> 63
2 -> (3, 0, 0, 100, 100, 1, 1791862006, 0x667dc486af9557a83488110fdb18fe653ba6d3be4773420590ee53b4a052fa45)   admin, multisig, timelock, price feed, shared signers, composite
3 -> false

3 findings, each with its replay

1. The tokenized stocks sampled on Robinhood Chain share one beacon whose thirteen roles are all held by plain keys, rooted in one admin key that can grant any of them with no delay

Five tokenized stocks and funds of the roughly 230 listed on Robinhood Chain are tracked (TSLA, AMZN, PLTR, NFLX, AMD). Each is a small proxy that forwards to the same beacon, so the authority sits on the beacon. Replaying the full history of the beacon, 274 events, gives thirteen roles (upgrade, pause, token pause, block, mint, burn and six whose names are not known), all held by plain keys, and not one role-admin change: every role hangs from the default admin, a key with no code and a nonce of 2. That admin cannot call the upgrade or block functions itself, but one grant with no delay makes it the holder of any role; a simulated call from it passes and the same call from another address reverts. Each of the five tokens has a composite of 1.

Replay it (public data, no key)
# the five tracked tokens read the same beacon from the EIP-1967 beacon slot
RH=https://rpc.mainnet.chain.robinhood.com
SLOT=0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
for t in 0x322f0929c4625ed5bad873c95208d54e1c003b2d 0x12f190a9f9d7d37a250758b26824b97ce941bf54 0x894e1ec2d74ffe5aef8dc8a9e84686accb964f2a 0xe0444ef8bf4ed74f74fd73686e2ddf4c1c5591e8 0x86923f96303d656e4aa86d9d42d1e57ad2023fdc; do cast storage $t $SLOT --rpc-url $RH; done | sort -u
# -> one word only, ending in the beacon address 0xe10b6f6b275de231345c20d14ab812db62151b00
# the default admin granted at block 7802, whether it holds the role now, and what it is
BEACON=0xe10b6f6b275de231345c20d14ab812db62151b00
HOLDER=$(cast logs --json --from-block 7802 --to-block 7802 --address $BEACON "RoleGranted(bytes32,address,address)" --rpc-url $RH | python3 -c "import json,sys; [print('0x'+l['topics'][2][-40:]) for l in json.load(sys.stdin) if int(l['topics'][1],16)==0]")
cast call $BEACON "hasRole(bytes32,address)(bool)" 0x0000000000000000000000000000000000000000000000000000000000000000 $HOLDER --rpc-url $RH
cast code $HOLDER --rpc-url $RH
cast nonce $HOLDER --rpc-url $RH
# -> true, then 0x (no code), then 2
# a simulated grant of the pauser role to itself: from the admin it passes, from another address it reverts (no transaction is sent)
ROLE=$(cast keccak PAUSER_ROLE)
cast call --from $HOLDER $BEACON "grantRole(bytes32,address)" $ROLE $HOLDER --rpc-url $RH
cast call --from 0x1111111111111111111111111111111111111111 $BEACON "grantRole(bytes32,address)" $ROLE $HOLDER --rpc-url $RH 2>&1 | head -1 | cut -c1-90
# -> 0x (it passes), then an execution reverted error

Notes: rotation_audit_2026-09-20-robinhood-chain-index15.md

2. The Safe that administers the Chainlink feeds sampled on Robinhood Chain is 4-of-9 with one analyzed module, and its score was corrected upward, in public, from 36 to 52

The Chainlink price-feed admin on Robinhood Chain is a Safe that needs 4 of 9 signatures and owns the 7 feeds sampled (57 are listed for the chain). It has one enabled module, a confirmed-transaction module whose functions are limited to its manager, read and simulated. On 21 September 2026 the repository found that two of its older scorers, this one and the LayerZero one, used a leftover multisig formula that was never documented; applying the one documented formula moved the composite from 36 to 52 and from 34 to 48. The note states that both values went up and that it is not a quiet improvement.

Replay it (public data, no key)
# the Safe: threshold, owners, enabled modules
RH=https://rpc.mainnet.chain.robinhood.com
CL=0xee27d5ae494300902d90454e8630a3f1c68c9c52
cast call $CL "getThreshold()(uint256)" --rpc-url $RH
cast call $CL "getOwners()(address[])" --rpc-url $RH | tr ',' '\n' | wc -l
cast call $CL "getModulesPaginated(address,uint256)(address[],address)" 0x0000000000000000000000000000000000000001 10 --rpc-url $RH
# -> 4, then 9, then one module address

Notes: finding_2026-09-21-safe-modules-and-guards.md · correction_2026-09-21-chainlink-layerzero-multisig-formula.md

3. Four Uniswap targets on Robinhood Chain are owned by the same code-less alias of the Uniswap Timelock that governs Uniswap on Arbitrum

The Uniswap V3 factory, V4 pool manager, UniswapX reactor and V2 fee setter tracked on Robinhood Chain all resolve to the Arbitrum-family alias of Uniswap's Ethereum Governance Timelock, an address with no code. The oracle once read this as a bare key; the correction is published. Four Robinhood targets, one Arbitrum target, three Base targets, two on Ethereum and one on Monad hang from that single Timelock: 11 tracked targets on 5 ecosystems.

Replay it (public data, no key)
# the V3 factory on Robinhood Chain is owned by an address with no code, equal to the alias of the Ethereum Timelock
RH=https://rpc.mainnet.chain.robinhood.com
OW=$(cast call 0x8366a39cc670b4001a1121b8f6a443a643e40951 "owner()(address)" --rpc-url $RH)
cast code $OW --rpc-url $RH
TL=$(cast call 0x000000000004444c5dc75cb358380d2e3de08a90 "owner()(address)" --rpc-url https://ethereum-rpc.publicnode.com)
python3 -c "print(hex((int('$TL',16) + 0x1111000000000000000000000000000000001111) % (1<<160)))"
echo $OW
# -> 0x, then the alias twice (0x2bad...46cd)

Notes: finding_2026-09-20-uniswap-one-timelock-eleven-targets.md · correction_2026-09-16-uniswap-bridge-alias.md

Targets

63 targets are tracked on Robinhood Chain, read from mainnet and scored by the oracle on Robinhood Chain. Bands of the composite score:

critical20
weak12
moderate23
decent3
strong5

Up to 12 targets, ordered by value where known, then by lowest score. Each name opens the contract page.

TargetCompositeBand
Robinhood Chain’s own L1 rollup authority57moderate
Robinhood Chain’s own L1 rollup authority57moderate
Robinhood Chain’s own L1 rollup authority57moderate
Robinhood Chain’s own L1 rollup authority57moderate
Robinhood Chain’s own L1 rollup authority57moderate
Robinhood Chain’s own L1 rollup authority57moderate
Morpho Blue (singleton, Robinhood Chain)55moderate
Morpho Grove x Steakhouse + Steakhouse Turbo USDG vaults40moderate
Morpho Steakhouse USDG vault44moderate
Morpho Grove x Steakhouse + Steakhouse Turbo USDG vaults66decent
Morpho Ethena x Steakhouse USDG vault70decent
Lighter (zkLighter) Escrow proxy54moderate

What is not read

Read from the public repository and the Watch at build, 2026-10-15 21:09 UTC. A finding is a dated reading with its replay, not an audit and not investment advice. All chain files · Dashboard.