Research / Chain files / Arbitrum

Arbitrum: who can change what the oracle reads

13 contracts on Arbitrum One are tracked and scored by an oracle on Arbitrum Sepolia: GMX, Aave, Compound, Pendle, Fluid, Uniswap and the Arbitrum Security Council. Arbitrum is where an address with no code can be a governor, and where one of our own published scores was wrong and had to be corrected.

What a track judge can check in two minutes

Three calls on the Arbitrum (Sepolia) 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 319507783: they change when the oracle is re-pushed.

RPC=https://sepolia-rollup.arbitrum.io/rpc
ORACLE=0x50840a7667baEa9D05ad4ae3dCeb384724b58720
TARGET=0x3c3d99fd298f679dbc2cecd132b4ec4d0f5e6e72   # GMX V2 (Synthetics) RoleStore

# 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 -> 13
2 -> (10, 0, 50, 52, 80, 19, 1791852875, 0x51163f3cce4612011bca19a651e2c0b650d9c670559f9036a37acfadf0532f54)   admin, multisig, timelock, price feed, shared signers, composite
3 -> false

3 findings, each with its replay

1. GMX V2's role store has a plain key among its timelock admins: the score originally published for it (71) missed that and was corrected

The score originally published for the GMX V2 role store, 71, followed a rule that checked one timelock controller. Re-derived from zero against Arbitrum One, that controller holds no role on the store. The live timelock admins are a 5-of-8 Safe, a 4-of-6 Safe and one plain key that GMX's own configuration file labels as a timelock admin. That key can signal a grant of the role-admin role, executable after the controller's 24-hour delay; the 5-of-8 Safe can cancel. The corrected score is far lower, and the contract page shows the current value.

Replay it (public data, no key)
# the timelock admins of the GMX V2 role store, and what each one is
ARB=https://arb1.arbitrum.io/rpc
STORE=0x3c3d99FD298f679DBC2CEcd132b4eC4d0F5e6e72
KEY=$(cast keccak $(cast abi-encode "f(string)" "TIMELOCK_ADMIN"))
for m in $(cast call $STORE "getRoleMembers(bytes32,uint256,uint256)(address[])" $KEY 0 10 --rpc-url $ARB | tr -d '[] ' | tr ',' ' '); do echo "$(cast code $m --rpc-url $ARB | cut -c1-12)  nonce $(cast nonce $m --rpc-url $ARB)"; done
# -> three members: two Safes (0x6080604052, nonce 1) and one with code 0x (a plain key, nonce above 70)

Notes: rotation_audit_2026-09-19-index0-gmx-rolestore.md

2. Arbitrum's own emergency root is a 9-of-12 Safe that can bypass the 8-day delay of the core timelock

The Arbitrum Security Council emergency Safe needs 9 of 12 signatures and holds the executor role on the L2 UpgradeExecutor. The ordinary core timelock of the chain has an 8-day delay; the emergency Safe can act without waiting for it. This is a documented part of Arbitrum's security model, not a flaw found here. Because this Safe is the emergency root of every protocol on the chain, its score is capped rather than raised, and it is tracked as a systemic target.

Replay it (public data, no key)
# the Safe: threshold and owners
ARB=https://arb1.arbitrum.io/rpc
SC=0x423552c0f05baccac5bfa91c6dcf1dc53a0a1641
cast call $SC "getThreshold()(uint256)" --rpc-url $ARB
cast call $SC "getOwners()(address[])" --rpc-url $ARB | tr ',' '\n' | wc -l
# -> 9, then 12
# it holds the executor role, and the core timelock's delay
cast call 0xCF57572261c7c2BCF21ffD220ea7d1a27D40A827 "hasRole(bytes32,address)(bool)" $(cast keccak EXECUTOR_ROLE) $SC --rpc-url $ARB
cast call 0x34d45e99f7D8c45ed05B5cA72D54bbD1fb3F98f0 "getMinDelay()(uint256)" --rpc-url $ARB
# -> true, then 691200 (8 days)

Notes: scored_targets_2026-09-17.md

3. Uniswap's governor on Arbitrum is an address with no code: the Arbitrum alias of the Ethereum Timelock, which this repository once wrongly called a vanity key

The owner chain of the Uniswap V3 factory on Arbitrum ends at an address that has no code. An earlier version of this repository described it as a vanity-ground bare key built to look like Uniswap's governance address. That was wrong, and the correction is published: the address is the Arbitrum L1-to-L2 alias of Uniswap's Ethereum Governance Timelock, computed as the L1 address plus 0x1111000000000000000000000000000000001111 modulo 2^160. An alias has no code by design, which a scorer that reads empty code as a plain key gets wrong.

Replay it (public data, no key)
# the alias, computed from the Uniswap Timelock on Ethereum (the owner of the V4 PoolManager there)
TL=$(cast call 0x000000000004444c5dc75cb358380d2e3de08a90 "owner()(address)" --rpc-url https://ethereum-rpc.publicnode.com)
python3 -c "print(hex((int('$TL',16) + 0x1111000000000000000000000000000000001111) % (1<<160)))"
# -> the alias address, 0x2bad...46cd
# the V3 factory on Arbitrum: its owner is an adapter, the adapter's owner is the alias, and the alias has no code
ARB=https://arb1.arbitrum.io/rpc
ADAPTER=$(cast call 0x1F98431c8aD98523631AE4a59f267346ea31F984 "owner()(address)" --rpc-url $ARB)
ALIAS=$(cast call $ADAPTER "owner()(address)" --rpc-url $ARB)
echo $ALIAS
cast code $ALIAS --rpc-url $ARB
# -> the alias address (0x2bad...46cd, as computed above), then 0x (no code)

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

Targets

13 targets are tracked on Arbitrum, read from mainnet and scored by the oracle on Arbitrum (Sepolia). Bands of the composite score:

critical3
weak1
moderate5
decent3
strong1

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

TargetCompositeBand
Arbitrum Security Council Emergency Safe64decent
Aave V3 Pool (Arbitrum)71decent
GMX V2 (Synthetics) RoleStore19critical
Fluid Liquidity (Arbitrum)59moderate
Uniswap V3 Factory (Arbitrum One)85strong
Compound V378decent
Pendle V243moderate
Camelot AMMv3 (Algebra) Factory31weak
GMX V1 Perps57moderate
Radiant Capital LendingPool (V2 Core)55moderate
GNSMultiCollatDiamond (verified source)4critical
OAdapter (verified source)8critical

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.