Skip to content

[WIP] docs: add Robinhood Earn contracts - #983

Open
tamtamchik wants to merge 2 commits into
mainfrom
docs/add-robinhood-earn-contracts
Open

[WIP] docs: add Robinhood Earn contracts#983
tamtamchik wants to merge 2 commits into
mainfrom
docs/add-robinhood-earn-contracts

Conversation

@tamtamchik

Copy link
Copy Markdown
Member

Adds Robinhood Chain contract addresses to the Earn deployments page.

@tamtamchik
tamtamchik requested a review from a team as a code owner August 27, 2026 15:06
@tamtamchik
tamtamchik requested a balanced review from Copilot August 27, 2026 15:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Robinhood Chain contract addresses to the Earn deployments documentation.

Changes:

  • Adds an earnUSDe – Robinhood deployment section.
  • Links nine contracts to the Robinhood Chain Blockscout explorer.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@TheDZhon

Copy link
Copy Markdown
Contributor

Codex:

  1. [P1] The actor table omits a direct verifier-root authority on Robinhood.

    The page says that its actor table lists the roles governing the vaults and that the addresses are shared across all chains. However, the Robinhood vault currently has two holders of SET_MERKLE_ROOT_ROLE: the documented timelock and the EOA 0xE98Be1E5538FCbD716C506052eB1Fd5d6fC495A3. The latter has no deployed bytecode and is absent from the actor table.

    Both independent RPC providers returned the same result at adjacent pinned blocks 47,564,255 and 47,564,256: role-holder count 2, the timelock as member 0, the EOA as member 1, and hasRole(...) == true for the EOA. This also matches the decoded initializer in the deployment config. Because Verifier.setMerkleRoot authorizes callers through this vault role, the EOA is a direct control path outside the documented timelock flow.

    Please either revoke this authority before publishing the deployment or document the additional actor and its direct authority explicitly.

  2. [P1] The displayed Safe quorums are not valid for Robinhood.

    The global table links readers to Ethereum Safe instances and reports Active Vault Admin as 3/8, Oracle Updater as 3/8, and Treasury as 4/7. The contracts at those same addresses on Robinhood currently report:

    Actor Documented Robinhood on-chain
    Active Vault Admin 3/8 3/6
    Oracle Updater 3/8 3/7
    Treasury 4/7 4/6

    Two independent RPC providers reproduced all three results at adjacent pinned blocks 47,564,064 and 47,564,065. Please make the actor/quorum documentation chain-specific, or otherwise show the Robinhood Safe configurations instead of presenting the Ethereum values as universal.

@tamtamchik

Copy link
Copy Markdown
Member Author
  1. Expected ATM, see feat: add earnUSDe Robinhood config state-mate#172
  2. Fixed Treasury msig quorum. Msigs are described for mainnet. Curators msigs may differ on other networks, as long as quorum sane we may ack issue.

@TheDZhon

Copy link
Copy Markdown
Contributor

Claude:

Bottom line: the address table is correct and complete, and the Treasury quorum fix is right. Two things left — one documentation-accuracy issue that should gate publishing, and one wording fix for the Quorum column.

Verified against chain state

Two things worth recording so nobody "fixes" them later:

  • ShareManager / FeeManager / RiskManager share addresses with the Plasma table by design, not by copy-paste. On Plasma those contracts report vault() == 0x49DAb986… (Plasma vault); on Robinhood the same addresses report vault() == 0xEe94bb5a… (Robinhood vault). Deterministic cross-chain deployment — both rows are correct.
  • The Treasury change 4/74/6 is correct. The Ethereum Safe itself is 4/6 at block 25847851, so the old value was simply stale — this is not a Robinhood value leaking into a mainnet row, and it matches what scripts/fetch-msig-quorums.js would write.

1. [P1] The Actors table omits a live verifier-root authority — please revoke before publishing

Confirming the earlier finding independently: the Robinhood vault has two holders of SET_MERKLE_ROOT_ROLE — the documented Timelock Controller and the EOA 0xE98Be1E5538FCbD716C506052eB1Fd5d6fC495A3, which has no deployed bytecode and appears nowhere in the Actors table.

Verifier.SET_MERKLE_ROOT_ROLE()             = 0xfc199f685d023b44b528c5fcb9cebfe292e64340dd5729b20761da4ad1e93024
vault.getRoleMemberCount(role)              = 2   -> [Timelock, 0xE98Be1E5...]
vault.hasRole(role, 0xE98Be1E5...)          = true
eth_call setMerkleRoot(...) from 0xE98Be1E5 = 0x                 (succeeds)
eth_call same from a control address        = revert 0xee90c468  (Forbidden)

Verifier.setMerkleRoot is gated only on vault().hasRole(SET_MERKLE_ROOT_ROLE, msg.sender), so this is a control path outside the documented Timelock flow.

On the "Expected ATM, see lidofinance/state-mate#172" disposition — that PR does not treat it as expected state. configs/lido-earn/mainnet/earnusd-vaults-robinhood.yaml asserts the Timelock alone and carries:

# WARN: mellow_deployer must renounce SET_MERKLE_ROOT_ROLE (on-chain count is 2)
*SET_MERKLE_ROOT_ROLE : [*earnUSDe_timelockController]

So the state layer records this as a required renunciation — that config is expected to fail against current chain state until it happens. (Credit where due: that WARN was in the first commit of #172, before any review, so it was self-identified.)

Two things make revoking-first the cheap option here rather than a doc workaround:

  • Current exposure is zero: merkleRoot == 0x0, allowedCalls == 0, queueLimit == 0, share totalSupply == 0. Nothing is at risk today.
  • Every sibling deployment already revoked it. Same role/EOA pair queried on Plasma, Mantle and Ethereum earnUSD: false on all three. Robinhood is the only one where it persists, and four of this EOA's five other deployment-time grants (DEFAULT_ADMIN, CREATE_SUBVAULT, ALLOW_SUBVAULT_ASSETS, SET_SUBVAULT_LIMIT) were revoked — so this looks like one missed step in an otherwise complete sweep.

Suggested: revoke, then publish, and no change to this page is needed. If the page must ship first, add a footnote naming the EOA and its authority — publishing a table introduced as "Roles governing the vaults" while a live root-setting authority is missing from it is a documentation-accuracy defect independent of the remediation timeline.

2. [P2] The Quorum column is Ethereum-specific, but the page presents it as universal

The header says "Addresses are shared across all chains", and every Safe link uses the safe=eth: prefix. Verified at pinned blocks — Ethereum 25847851, Robinhood 47596577:

Actor Documented Ethereum Robinhood
Active Vault Admin 3/8 3/8 ✅ 3/6
Oracle Updater 3/8 3/8 ✅ 3/7
Treasury 4/6 4/6 ✅ 4/6 ✅

All eight other actor rows match on both chains. So the documented numbers are correct for Ethereum — the issue is purely that the page doesn't say that's what they are.

One note on the "Curators msigs may differ on other networks" rationale: the two actual divergences aren't curators — they're Active Vault Admin and Oracle Updater, both core governance actors. The Curator (earnUSDc, earnUSDe) Safe is 3/7 on both chains.

This is also already settled by the repo's own tooling, which is worth making explicit rather than leaving implicit. scripts/fetch-msig-quorums.js resolves the chain from the safe=<chain>: prefix and rewrites drifted cells, so it will always maintain these as Ethereum values — and robinhood is not in its CHAIN_RPCS, so a Robinhood-prefixed Safe link would fail with unsupported chain: robinhood. Per-chain quorum rows would need the chain registered there first.

Suggested minimal fix — replace the section lead-in:

-Roles governing the vaults. Addresses are shared across all chains.
+Roles governing the vaults. The same actor addresses are used on every chain where the
+actor is deployed. The **Quorum** column reflects the Ethereum Safe configuration; owner
+sets and thresholds can differ per chain — on Robinhood, Active Vault Admin is 3/6 and
+Oracle Updater is 3/7.

3. [nit] "Shared across all chains" is literally false for three rows

Three actor rows have no code at all on Robinhood: Curator (earnETH, earnUSD), Curator (stRATEGY), Curator (GGV). That's expected — those curate vaults that don't exist on this chain — but it's another reason to soften the blanket sentence, which the wording above already covers.

4. [nit] Cosmetic table width

The new table's separator row is two characters wider than its header row, and both are wider than the Plasma/Mantle tables. No functional impact and no CI impact (npm run format only targets docs/, not earn/), so purely take-it-or-leave-it.

@tamtamchik tamtamchik changed the title docs: add Robinhood Earn contracts [WIP] docs: add Robinhood Earn contracts Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants