Research / Chain files / Ethereum L1

Ethereum L1: who can change what the oracle reads

34 contracts on Ethereum mainnet are tracked and scored by an oracle on Sepolia: the deepest set, with Aave, Compound, Morpho, Lido, the stablecoin issuers and Ethena. Here the interesting questions are who can move the prices a lending market reads, and which seat one committee holds on several chains.

What a track judge can check in two minutes

Three calls on the Ethereum L1 (Sepolia) oracle, replayed at the block the Watch read (11922317). They need a public RPC and cast (Foundry) and nothing from us. If your RPC does not keep that block, drop --block: the answers change when the oracle is re-pushed.

RPC=https://ethereum-sepolia-rpc.publicnode.com
ORACLE=0xB6F8474ccC71AF477c31c2DF663B3942ddfbf906
TARGET=0x2f39d218133afab8f2b819b1066c7e434ad94e9e   # Aave V3 Ethereum Pool

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

# 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 --block 11922317

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

Expected

1 -> 34
2 -> (78, 100, 55, 52, 80, 78, 1791855910, 0x46a1a85b619e7db0f81e543674e8aa0a3a1afba36b7500fe2cb256e0024db645)   admin, multisig, timelock, price feed, shared signers, composite
3 -> false

3 findings, each with its replay

1. Aave V3 prices 44 of its 67 reserves with its own adapters, and the stewards that can set them answer to a 2-of-2 Safe: one plain key and one Safe

On 1 October 2026, 44 of the 67 reserves of Aave V3 Ethereum Core ($14.71B, 58% of supply) were priced by Aave's own adapters, each with a known setter reachable by the RISK_ADMIN and POOL_ADMIN holders of the ACLManager. The steward contracts that hold RISK_ADMIN answer to one risk council: a 2-of-2 Safe with no module, whose two owners are a plain key and a 1-of-3 Safe. The update path limits what the council can do in one step; simulated updates beyond those limits were refused. This is disclosed next to the score and is not part of the composite.

Replay it (public data, no key)
# a steward holds RISK_ADMIN on the Core ACLManager
ETH=https://ethereum-rpc.publicnode.com
ACL=0xc2aaCf6553D20d1e9d78E365AAba8032af9c85b0
STEWARD=0x13a9cc64344b02bacc5ad9cf38b5711f1b9ec3d4
cast call $ACL "hasRole(bytes32,address)(bool)" $(cast keccak RISK_ADMIN) $STEWARD --rpc-url $ETH
# -> true
# the steward's council: threshold, number of owners, modules
COUNCIL=$(cast call $STEWARD "RISK_COUNCIL()(address)" --rpc-url $ETH)
cast call $COUNCIL "getThreshold()(uint256)" --rpc-url $ETH
cast call $COUNCIL "getOwners()(address[])" --rpc-url $ETH | tr ',' '\n' | wc -l
cast call $COUNCIL "getModulesPaginated(address,uint256)(address[],address)" 0x0000000000000000000000000000000000000001 10 --rpc-url $ETH
# -> 2, then 2, then modules: []
# what each owner is: 0x means a plain key, long code means a contract
for o in $(cast call $COUNCIL "getOwners()(address[])" --rpc-url $ETH | tr -d '[] ' | tr ',' ' '); do cast code $o --rpc-url $ETH | cut -c1-12; done
# -> 0x for one owner, 0x6080604052 for the other

Notes: finding_2026-10-01-aave-capo-price-authority.md

2. A tracked Ethena target turned out to be USDe's retired minter: a sweep found it, the live minter was added, the old entry is left to expire

Earlier, the oracle scored an Ethena minting contract that a 5-of-10 Safe owns directly with no timelock. USDe has had two minter updates in its life, and since 8 July 2024 its minter is another contract; the old one cannot mint, a simulated mint from it reverts, and it holds no collateral. The live minter was added as a new target and the old target is retired by ceasing to refresh it, so the oracle now reports it as stale. USDe itself is owned by a timelock controller with a 24-hour delay. The contract page of the live minter shows its current score.

Replay it (public data, no key)
# USDe's minter and its owner
ETH=https://ethereum-rpc.publicnode.com
USDE=0x4c9EDD5852cd905f086C759E8383e09bff1E68B3
cast call $USDE "minter()(address)" --rpc-url $ETH
OWNER=$(cast call $USDE "owner()(address)" --rpc-url $ETH)
cast call $OWNER "getMinDelay()(uint256)" --rpc-url $ETH
# -> 0xe3490297a08d6fC8Da46Edb7B6142E4F461b62D3, then 86400 (24 hours)
# the oracle on Sepolia: the retired target is stale, the live minter is not
ORACLE=0xB6F8474ccC71AF477c31c2DF663B3942ddfbf906
SEP=https://ethereum-sepolia-rpc.publicnode.com
cast call $ORACLE "isStale(address)(bool)" 0x2cc440b721d2cafd6d64908d6d8c4acc57f8afc3 --rpc-url $SEP
cast call $ORACLE "isStale(address)(bool)" 0xe3490297a08d6fc8da46edb7b6142e4f461b62d3 --rpc-url $SEP
# -> true, then false (as read on 14 October 2026)

Notes: finding_2026-09-20-ethena-live-minter.md

3. Compound V3's USDC pause guardian is one 5-of-9 committee on Ethereum, Arbitrum and Base

On each of the three chains the pause guardian of the Compound V3 USDC market is a Safe that needs 5 of 9 signatures, at three different Safe addresses with identical owner sets. The seat can pause supply, transfer, withdraw, absorb and buy instantly, outside the timelock: an availability power, not a way to redirect funds. One quorum can therefore freeze Compound V3 on three chains at once. The oracle publishes 80 for shared signers on each of the three markets for that reason.

Replay it (public data, no key)
# the guardian of each market, its threshold and number of owners
ETH=https://ethereum-rpc.publicnode.com
G1=$(cast call 0xc3d688B66703497DAA19211EEdff47f25384cdc3 "pauseGuardian()(address)" --rpc-url $ETH); echo $G1
G2=$(cast call 0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf "pauseGuardian()(address)" --rpc-url https://arb1.arbitrum.io/rpc); echo $G2
G3=$(cast call 0xb125E6687d4313864e53df431d5425969c15Eb2F "pauseGuardian()(address)" --rpc-url https://mainnet.base.org); echo $G3
# -> three different Safe addresses (0xbbf3...012c, 0x78E6...1a9d, 0x3cb4...d2ee)
# the owner sets are identical (the diff prints nothing)
n(){ tr -d '[] ' | tr ',' '\n' | tr A-F a-f | sort; }
diff <(cast call $G1 "getOwners()(address[])" --rpc-url $ETH | n) <(cast call $G2 "getOwners()(address[])" --rpc-url https://arb1.arbitrum.io/rpc | n)
diff <(cast call $G1 "getOwners()(address[])" --rpc-url $ETH | n) <(cast call $G3 "getOwners()(address[])" --rpc-url https://mainnet.base.org | n) && echo identical
cast call $G1 "getThreshold()(uint256)" --rpc-url $ETH
# -> identical, then 5

Notes: finding_2026-09-20-compound-pause-guardian-three-chains.md

Targets

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

critical5
weak1
moderate10
decent10
strong8

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

TargetCompositeBand
Lido stETH80strong
Aave V3 Ethereum Pool78decent
WBTC (Wrapped BTC)52moderate
EigenLayer StrategyManager60decent
MakerDAO / Sky governance (MCD_PAUSE)81strong
SparkLend PoolAddressesProvider78decent
Morpho Blue (singleton, Ethereum L1)56moderate
Ethena EthenaMinting (retired minter)52moderate
Ethena LayerZero OFTAdapters57moderate
Ethena LayerZero OFTAdapters57moderate
Ethena LayerZero OFTAdapters57moderate
Ethena EthenaMinting (USDe live minter)73decent

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.