Notes / Corrections

Correction (2026-09-16): Uniswap stack's "vanity-ground bare EOA" was wrong

Corrections2026-09-16Chain: Ethereum, Arbitrum

Source: data/correction_2026-09-16-uniswap-bridge-alias.md in the repository, rendered here without changes. Source file

What was published, and what was wrong with it#

score_uniswap_v3_factory, score_uniswap_v4_poolmanager, score_uniswapx_reactor, and score_uniswap_v2_factory_feetosetter all resolve owner()/feeToSetter() to 0x2BAD…46CD. Since batch 4, this project has described that address in scorers.py, dashboard/index.html, and README.md as a "vanity-ground bare EOA" built to resemble Uniswap's real governance address closely enough to fool anyone skimming a truncated address -- implying a compromised or maliciously-mined private key with zero real protection.

That characterization is wrong.

What's actually true, re-verified today (not just recalled)#

0x2BAD…46CD is the legitimate Arbitrum L1->L2 address alias of Uniswap's real Ethereum-mainnet Governance Timelock, 0x1a9C…35BC. The Arbitrum canonical-bridge aliasing formula is deterministic, not a coincidence:

alias = (L1_address + 0x1111000000000000000000000000000000001111) mod 2^160

Re-derived exactly today:

l1 = int('1a9C8182C09F50c8318d769245bEA52c32BE35BC', 16)
offset = int('1111000000000000000000000000000000001111', 16)
alias = (l1 + offset) % (2**160)
# -> 0x2bad…46cd  (matches, case-insensitive)

Live bytecode check today (eth_getCode, both addresses, 2026-09-16):

The empty bytecode at the L2 alias is expected, not suspicious: an L1->L2 alias is a msg.sender identity that the Arbitrum canonical bridge assigns to a message when the L1 contract relays it -- it is never itself the target of a contract deployment, so eth_getCode on it will always return empty, exactly as if it were a bare EOA. This is precisely why the earlier "vanity-ground bare EOA" read was made -- the mechanical is_eoa() check (no bytecode = true) was never wrong on its own, but the narrative built on top of it was.

What this does NOT mean#

What changed in this commit#

Open follow-up (not done here)#

A dedicated re-scoring pass needs: (1) Uniswap's actual Governor Bravo parameters (proposal threshold, quorum, voting delay, timelock delay -- publicly documented, not yet re-verified by this project), (2) a decision on whether/how the existing 3-dimension scoring model should represent "external L1 DAO governance via bridge alias" as a distinct authority shape, and (3) a check on whether any other Robinhood-Chain-tracked target flagged as a "bare EOA" is actually the same aliasing pattern rather than a real EOA (Curve's controller was checked today and ruled OUT via real Arbitrum transaction history -- see the corrected score_curve_dex docstring -- but no other "bare EOA" target has been checked against this pattern yet).

RESOLVED 2026-09-17 -- items (1) and (2) closed#

Added scripts/lib/web3_utils.py::arbitrum_l1_l2_alias() (the alias formula as a reusable, re-derived-every-run function, not a one-off hand calculation) and scripts/lib/scorers.py::_score_uniswap_bridge_alias_root(), a shared scoring path for all four Uniswap-stack surfaces. It re-confirms the alias match AND independently re-derives the L1 Timelock/GovernorBravo live on every run (own Ethereum-mainnet Web3 instance, exactly the discipline score_rollup_l1_authority() already used in this file) -- re-verified live today: delay() = 172,800s (2 days), quorumVotes() = 40,000,000 UNI, both unchanged since the original correction.

Uses the SAME admin_key=80/multisig=100/timelock=75 convention already established for this identical authority shape elsewhere in this project (chains/base-ecosystem /scorers.py's score_uniswap_v3_factory_base(), reached via OP-stack cross-domain messaging instead of an Arbitrum alias, and confirmed directly on L1 itself in chains/ethereum-l1/scorers.py's score_uniswap_v3_factory()) -- not a new number invented for this correction. If the alias stops matching or the L1 re-read fails on any future run, this degrades to the original bare-EOA-equivalent scoring rather than silently keeping the higher score.

All four Uniswap-stack surfaces now score 85/100 (up from 2/100), each independently re-verified live via python3 against rpc.mainnet.chain.robinhood.com

updated to match; dashboard/index.html's static score references were already correct (they read live from the oracle) but its Uniswap card copy is updated in the same commit as this note.

RESOLVED 2026-09-17 -- item (3) closed: no other target matches this pattern#

Swept every remaining target whose notes mention a bare EOA (scripts/lib/scorers.py::score_all() output, live): Pendle V2's MarketFactoryV6.owner(), Lighter Escrow's securityCouncilAddress, Spark Savings USDG's executor, and Ramses CL V2's AccessHub owner. For each, computed the REVERSE alias (address - offset mod 2^160) and checked for real bytecode at that hypothetical L1 address on Ethereum mainnet, plus each address's real transaction count on Arbitrum mainnet (the same independent-activity signal that ruled Curve's controller OUT in the original correction: a genuine bridge alias can never have organic, independently-signed activity of its own on Arbitrum).

Result: none reverse-alias to a real deployed L1 contract (0 bytecode at every computed reverse address), and Ramses' AccessHub owner specifically has 666 real transactions on Arbitrum mainnet -- strong, independent confirmation it's a real, actively-used key, not a mathematical alias byproduct (the same signal, even stronger, as Curve's 287-tx ruling). All four remain correctly scored as bare EOAs; the Uniswap-stack correction in this document does not generalize to any other tracked target.

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