Notes / Findings

Finding (2026-09-20): crossExposureScore now folds cross-ecosystem overlaps on every EVM ecosystem, and 11 scorer outputs moved

Findings2026-09-20Chain: Ethereum, Base, Arbitrum, Monad, Plasma, Tempo, Robinhood ChainLinked: Ethena EthenaMinting (USDe live minter), Ethena EthenaMinting (retired minter)

Source: data/finding_2026-09-20-cross-exposure-convention-and-score-adjustments.md in the repository, rendered here without changes. Source file

Status: decided and implemented in the scorers on 2026-09-20 (commit 042f805), and pushed on-chain the same day (section 9: six transactions, all status 1, read back with getScore() on all 7 oracles with 0 differences). This file was first written before the push, when the deployed testnet oracles still held the "Old" values in section 4 (read live, section 6); those are now history, and every "New" value in section 4 is the on-chain value.

1. The question#

Two conventions for crossExposureScore coexisted, and nobody had chosen between them:

The same committee therefore had a different number depending on the chain a consumer happened to read. The 9-signer Aave PayloadsController.guardian() committee scored 80 on Arbitrum, Base, Plasma and Monad and 100 on Ethereum L1. The 9 Morpho Association signers scored 80 on Base and 100 on Ethereum L1, Robinhood Chain and Tempo. The open question was recorded in data/finding_2026-09-19-aave-guardian-ethereum-l1-fifth-chain-and-verifier-corrections.md section 1 and left for the maintainer.

2. Decision and rationale#

Cross-ecosystem overlaps are folded into crossExposureScore, on every EVM ecosystem.

The field's documented meaning is "100 = no root signer shared with any other tracked target" (the comment on the field in src/AuthorityRiskOracle.sol, and METHODOLOGY.md). "Any other tracked target" is not limited to the same chain, and this project's own thesis (the SolGov comparison in README.md) is that a key shared across chains is the finding a per-chain tool cannot make. A field whose meaning changes with the chain is not something a consumer contract can gate on. Folding everywhere makes the number mean one thing; note-only everywhere would have kept a real, already-found shared-key risk out of the score.

3. The exact rule#

4. What moved (old on-chain value, then new value, now on-chain)#

Fields are admin / multisig / timelock / oracle / cross / composite. Old values were read from each testnet oracle with getScore() on 2026-09-20, before the push. The "New" column was the scorer output when this table was written and is the on-chain value since the push (section 9).

OracleTargetOld (on-chain before the push)New (scorer, then on-chain)Why
ArbitrumRadiant V2 LendingPool 0xE23B4AE3...688655/100/55/100/100/6965/95/0/100/100/55Pool-admin Safe path found and scored (section 5)
MonadAave V3 Pool 0x34793Fb9...A61565/100/50/100/80/7165/75/0/100/80/49PROTOCOL_GUARDIAN Safe also holds POOL_ADMIN (section 5)
Ethereum L1Aave V3 Pool 0x2f39d218...4E9e78/100/55/100/100/7878/100/55/100/80/78Both Aave committees also sit on other chains
Ethereum L1Morpho Blue 0xBBBBBbbB...FFCb65/95/0/100/100/5565/95/0/100/80/55Same 9 Morpho Association signers as Base, Robinhood Chain, Tempo
Robinhood ChainPendle V2 0x544BF81c...EeE270/45/0/100/100/4270/45/0/100/80/42Same Safe address and 5 owners as Plasma's Pendle
Robinhood ChainCurve DEX 0x8271e06E...E8aD2/0/0/100/100/12/0/0/100/80/1Same bare EOA 0xabc336d4...1cbF and factory address as Monad's Curve
Robinhood ChainMorpho Blue 0x9D53d5E3...101065/95/0/100/100/5565/95/0/100/80/55Same 9 signers as Ethereum L1, Base, Tempo
PlasmaPendle Router + Market Factory V6 0x88888888...F94665/55/0/100/100/4365/55/0/100/80/43Same Safe 0x7877AdFa...75Ac and owners as Robinhood Chain's Pendle
PlasmaEuler V2 eVaultFactory 0x42388213...EeA370/80/68/100/100/7270/80/68/100/80/72DAO Safe (8 signers) identical to Monad's Euler DAO signer snapshot of 2026-09-19
PlasmaEuler V2 AccessControlEmergencyGovernor 0x9b3CeB22...Da2c68/80/55/100/100/6868/80/55/100/80/68Same DAO Safe signers
TempoMorpho Blue core 0x10EE9AAC...8f9765/96/0/100/100/5565/96/0/100/80/559 owners equal the Ethereum L1, Base, Robinhood Chain Morpho Safe owners (the Safe address differs, so the snapshot holds owners, not an address)

Nine rows change only crossExposureScore, and no composite moves for them (it is not a composite input). Two rows change the score model. Base is unchanged. Every other target on every ecosystem is unchanged, re-run live per chain. The same push also carried the Ethereum L1 Ethena change (a new target, the live minter, and a retired old one), which is not one of these 11 rows; see section 9 and data/finding_2026-09-20-ethena-live-minter.md.

5. The two score-model changes behind rows 1 and 2#

These came from data/finding_2026-09-20-unscored-role-sweep.md (sections 1 and 3), where the maintainer decision they needed is now made.

Radiant (Arbitrum). The scorer credited only PoolAddressesProvider.owner(), a 72h TimelockController (0x27fC8f3B...Aff92, getMinDelay() 259200). The LendingPoolConfigurator's onlyPoolAdmin functions (token-proxy upgrades, freeze, caps, rate strategy) are gated by getPoolAdmin() instead, 0x111C…2177, a 4-of-11 Safe. That Safe holds PROPOSER and CANCELLER on the timelock and 6 of its 11 owners hold EXECUTOR (read live 2026-09-20), but the timelock does not bind the pool-admin path, so it is scored as a Safe with no timelock: admin 65 (threshold >= 3), multisig min(100, 4*15 + 7*5) = 95, timelock 0. The timelock still gates the provider-level owner functions, which the note discloses. getEmergencyAdmin() (0xDdF60973...0928) is a 1-of-5 Safe whose 5 owners are all owners of the 4-of-11; it can only pause, so it is disclosed and not scored. An unread pool admin or provider owner degrades to 20/0/0 instead of keeping a confident score. crossExposureScore stays 100: the pool-admin and emergency-admin owner sets were compared with 70 root-signer groups on 7 ecosystems and none matched, which is "found none", not proof of none.

Aave V3 (Monad). The PROTOCOL_GUARDIAN Safe 0xc887…7e74 (4-of-7, read live) holds POOL_ADMIN in the ACLManager 0xa9fEe192...3d95B (isPoolAdmin and isEmergencyAdmin both true, read live), so it acts with no timelock. Scored as a Safe with no timelock: admin 65, multisig min(100, 4*15 + 3*5) = 75, timelock 0. The admin score is min(DAO-path score, Safe rule), so a broken Executor loop is never raised. If isPoolAdmin reads false the scorer returns the previous 65/100/50 (71); if it cannot be read it degrades to 20/20/0 (14) with a DEGRADED note. crossExposureScore stays 80 (the 9-signer guardian committee).

Two more changes the same day touched notes only, no score: Plasma's Pendle scorer read storage slot 0 as the "EIP-1967 admin slot" and so always printed "NOT independently confirmed to converge"; it now reads the canonical slot (0xb53127...6103), which resolves live to ProxyAdmin 0xA28c08f1... whose owner() is the Safe 0x7877.... Tempo's score_all() used to overwrite each entry's notes and now merges them, so the cross-chain finding on the Morpho core is kept next to chainCappedComposite.

6. How each was verified#

7. What is NOT done (and what has since been done)#

8. Residual gaps#

9. Pushed on-chain 2026-09-20#

The maintainer ran the re-push scripts on 2026-09-20, roughly 02:15 to 02:40 Paris time. Six oracle writes landed and every transaction has status 1:

OracleOracle addressTransactionBlock
Ethereum L1 (Sepolia)0xB6F8474ccC71AF477c31c2DF663B3942ddfbf9060x14b0466ab52a7bf6cab54253cda92c73d52ca1b88957632911d79136b99b7a9811,740,952
Arbitrum (Sepolia)0x50840a76...87200x308ad2c8fa527a0e5c30139b6b154ff48462cd046df9840a17f25775c21fbd23310,697,409
Plasma testnet0x50840a76...87200xa2e8f4f12ccbe3740734bccfa5ee9cf4c20018f5a453a38c00e77a8583cb3de434,034,832
Monad testnet0x50840a76...87200xa93596300b689383572880cd4de76ee2fc7bce68ac5791db38501b07b987a01964,022,942
Tempo Moderato0x50840a76...87200xcdc3cc092d5e641de9407484a929ab40732799e0ae043f9fdea8d7ef400aa44b36,054,236
Robinhood Chain testnet0x9BF45734...7f520x020cf83c8a007afaef0eadf7ecb271432c80f1ffd9bfafed984ab6eca17c4978121,858,777

Base was NOT re-pushed: no Base score changed. api/scores.json (the Robinhood Chain snapshot) was regenerated by the push script with the Robinhood transaction hash and block above.

What each write put on-chain. The 11 rows of section 4, exactly as their "New" column (Radiant 65/95/0/100/100/55 on Arbitrum; Aave V3 Monad 65/75/0/100/80/49; on Ethereum L1 Aave V3 78/100/55/100/80/78 and Morpho Blue 65/95/0/100/80/55; on Plasma cross 80 for Pendle, the Euler eVaultFactory and the Euler AccessControlEmergencyGovernor; Tempo's Morpho Blue core 65/96/0/100/80/55; on Robinhood Chain cross 80 for Pendle V2, Curve DEX and Morpho Blue, composites unchanged), plus one change that is not among the 11: Ethereum L1 gained a new target, index 14, Ethena EthenaMinting (USDe live minter) 0xe3490297a08d6fC8Da46Edb7B6142E4F461b62D3, scored 65/100/55/100/20/73. What was wrong with the old target, the verified authority and the scoring reasons are in data/finding_2026-09-20-ethena-live-minter.md.

The retired Ethena target stays on the oracle. Index 3 (0x2CC440b721d2CaFd6D64908D6d8C4aCC57F8Afc3, 55/100/0/100/20/52) is no longer refreshed. The oracle contract has no removal function, so the entry stays as a historical reading and turns stale (isStale true) nine days after its last update, which was the 2026-09-19 push (16:39 UTC), that is after 2026-09-28 16:39 UTC. Until then a raw getScore() on it still returns a normal-looking, not-stale score for a contract that has not been USDe's minter since 2024-07-08. Ethereum L1 therefore has 15 tracked targets of which 14 are actively refreshed.

Read-back. After the six writes, plain getScore() (no key, no transaction) was read on all 7 oracles. Every oracle equals the scorer output of commit 042f805 exactly: 0 differences across Ethereum L1 (14 of 14 refreshed targets), Arbitrum (9), Plasma (9), Monad (9), Tempo (14) and Robinhood Chain (58); Base is unchanged. Re-read once more for this write-up on 2026-09-20 at about 02:46 CEST: Ethereum L1 Ethena live minter 65/100/55/100/20/73, Aave V3 78/100/55/100/80/78, Morpho Blue 65/95/0/100/80/55 (all three lastUpdated 2026-09-20 00:16:38 UTC) and Arbitrum Radiant 65/95/0/100/100/55 (lastUpdated 00:17:34 UTC), matching the scorer.

Tracked-target counts after the push (python3 scripts/live_target_counts.py, plain eth_call): Robinhood Chain 58, Ethereum L1 15, Arbitrum 9, Base 9, Tempo 14, Plasma 9, Monad 9, a total of 123 tracked targets on 7 oracles, of which 122 are actively refreshed (the retired L1 index 3 is the difference; the script prints the 123 and does not subtract it).

Not an audit, not investment advice. All notes · Back to the dashboard.