Notes / Findings
Source: data/finding_2026-09-20-uniswap-one-timelock-eleven-targets.md in the repository, rendered here without changes. Source file
Found during the registry coverage pass, by asking the one question the cross-exposure field exists for: which tracked targets share a root, across ecosystems, that the oracle still scores as if they did not?
Uniswap's Ethereum L1 Governance Timelock (0x1a9C…35BC, 2-day delay, GovernorBravo behind it) is the root authority of every Uniswap target this oracle tracks, on five ecosystems, reached through three different bridges:
| Ecosystem | Tracked targets | How the Timelock is reached |
|---|---|---|
| Ethereum L1 | V3 Factory, V4 PoolManager | directly (V4) or through a fee adapter (V3) |
| Arbitrum | V3 Factory | the Arbitrum alias 0x2BAD8182...46CD of the Timelock (an alias has no code) |
| Base | V3 Factory, V2 Factory, V4 PoolManager | one OP-stack forwarder 0x31FAfd48...72A9 whose slot 1 is the Timelock |
| Robinhood Chain | V3 Factory, V4 PoolManager, UniswapX reactor, V2 feeToSetter | the same Arbitrum-family alias |
| Monad | V4 PoolManager | a Wormhole message receiver whose trusted Ethereum sender is owned by the Timelock |
That is 11 targets under one root identity, on more ecosystems than any other single root this oracle tracks.
crossExposureScore read 80 on the two Ethereum L1 targets (V3 and V4 share a root group inside L1) and 100 on the other nine (Arbitrum 1, Base 3, Robinhood Chain 4, Monad 1). The 100 came from a documented rule that a target that is DAO or Timelock rooted "has no Safe signer set, so it is not counted". That rule was written when the cross-ecosystem sweep could only compare Safe owner sets. It was a limit of the tool, not a judgement that the Timelock is not a shared root. The field's own definition ("does this target's root authority also control another tracked target") is met exactly.
The 2026-09-20 convention (a shared root across ecosystems folds into crossExposureScore as a flat 80, never raising a lower in-ecosystem value) now covers a shared contract root as well as a shared committee. Nothing else about the score moves: compositeScore does not use crossExposureScore, so every Uniswap composite is unchanged (confirmed by the live drift check below: only the cross column differs).
uniswap_stack targets take the same flat 80 through the group's hand-set, dated cross_ecosystem flag, like Pendle, Morpho Blue and Curve.messageSender() is the known Ethereum sender, and that sender's owner() is the Timelock, both hops read live; the first version of this fold relied on a dated snapshot for the Ethereum side, and the docstring named the getter sender(), which reverts, instead of messageSender()). A degraded run keeps 100 and says why.uniswap_dao_timelock group in each of the L1, Arbitrum, Base and Monad registries, holding the Timelock (and on Arbitrum its alias) as a single root identity, plus the Timelock added to Robinhood Chain's group, so scripts/check_cross_ecosystem_overlap.py reports the shared root instead of missing it.Scorer and registry changes are on GitHub. The deployed testnet oracles still hold the previous values (Uniswap cross 100 on Arbitrum, Base and Monad; Robinhood Chain's four also read 100). They reach the chains at the next re-push of Arbitrum, Base, Monad and Robinhood Chain, run by the owner of the updater key; a live drift check on 2026-09-20 shows exactly these crossExposureScore differences and no others beyond the Compound V3 ones already documented.
Not an audit, not investment advice. All notes · Back to the dashboard.