Add OETHb migration contracts - #2909
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #2909 +/- ##
==========================================
+ Coverage 44.63% 50.57% +5.94%
==========================================
Files 110 123 +13
Lines 4920 5811 +891
Branches 1362 1641 +279
==========================================
+ Hits 2196 2939 +743
- Misses 2721 2869 +148
Partials 3 3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
sparrowDom
left a comment
There was a problem hiding this comment.
Minor comments otherwise LGTM
| /// must equal the vault (enforced by the require below); Master always forwards the | ||
| /// received bridgeAsset to `vaultAddress` on the leg-2 ack. | ||
| /// | ||
| /// Only the `remoteStrategyBalance` slice is drawable here: `_amount` must be |
There was a problem hiding this comment.
Proposed change of this section:
/// Drawable bound is `_drawableRemoteBalance()` = `remoteStrategyBalance +
/// min(0, bridgeAdjustment)`, NOT `remoteStrategyBalance` alone:
/// - a NEGATIVE `bridgeAdjustment` (net BRIDGE_OUT) is folded in, lowering the cap so we
/// never request more shares than Remote can actually unwrap (else it NACKs on Remote);
/// - a POSITIVE `bridgeAdjustment` is excluded here even though `checkBalance` counts it
/// (that + local bridgeAsset can report more). To realise it, `requestSettlement()`
/// first (folds bridgeAdjustment into remoteStrategyBalance) and/or use the local
/// bridgeAsset, then withdraw.
| /// on a WITHDRAW_CLAIM_ACK (the bridgeAsset is still held when this is computed). | ||
| /// `oTokenAmount` is in OToken (18dp) units, matching `_viewCheckBalance`; | ||
| /// `_yieldOnlyBaseline()` is the `oTokenAmount == 0` case. | ||
| /// @dev Clamps to 0 rather than reverting on a negative. `_viewCheckBalance - bridgeAdjustment` |
There was a problem hiding this comment.
suggested fix:
/// @dev Clamps to 0 rather than reverting on a negative. `_viewCheckBalance - bridgeAdjustment`
/// is principal + yield + retained fees and is never *economically* negative **while
/// (w)OTokens are up-only** — the only thing that pushes it a few wei negative is the
/// wOToken 4626 rounding against the strategy (~1 wei per BRIDGE_IN floor / BRIDGE_OUT
/// ceil) once a `withdrawAll` drains it near 0. Reverting on that dust would freeze the
/// serialized yield channel, so we clamp (matches checkBalance-never-reverts).
/// CAVEAT: a real negative rebase (loss/slashing) would make this genuinely negative; the
/// clamp then masks it, and because Master re-adds its OWN `bridgeAdjustment`,
/// `checkBalance` OVER-reports by ~`bridgeAdjustment` (not 0). Out of scope while
/// (w)OTokens never negative-rebase; revisit (signed baseline) if that changes. See DESIGN §3.10.
| leg-2 claim ack lands. | ||
|
|
||
| For `withdrawAll` (vault or governor sweep), `_withdrawRequest` is called with | ||
| `min(remoteStrategyBalance, inboundAdapter.maxTransferAmount())` so a sweep |
There was a problem hiding this comment.
this should be
`min(_drawableRemoteBalance(), inboundAdapter.maxTransferAmount())` so a sweep
| Master->>Master: _processWithdrawClaimAck success:<br/>_markYieldNonceProcessed(N+2)<br/>pendingWithdrawalAmount = 0<br/>remoteStrategyBalance = yieldBaseline | ||
| Master->>Vault: «WETH» transfer (forwards its full bridgeAsset balance) | ||
| Note over Master: safeTransfer(vaultAddress, balanceOf(this))<br/>emit Withdrawal(WETH, WETH, claimed) | ||
| else queue not yet matured (NACK) |
There was a problem hiding this comment.
proposed change:
Leg-2 NACKs on four conditions, not just an immature queue:
else NACK — outstandingRequestId != EMPTY / amount == 0 / bridgeAssetHeld < amount / ship out of [min,max]
| ) { | ||
| return; | ||
| } | ||
| // Best-effort: a mid-migration cleared inbound adapter must no-op the sweep, not |
There was a problem hiding this comment.
Proposed change:
// Best-effort no-op if the inbound adapter isn't wired yet (pre-configuration only —
// setInboundAdapter now rejects zero, so it can't be cleared mid-migration).
Both files existed as real files. CLAUDE.md held all 298 lines of real guidance; AGENTS.md held 40 lines written on another machine. Merge decisions: - Workflow guidance (prettier, smallest-verification, do-not-reformat): present in both, word for word. Kept CLAUDE.md's copy. - 'Run repo commands from /Users/nicholasaddison/.../contracts' loses to 'Run repo commands from contracts/'. The absolute path is another engineer's home directory and is wrong everywhere but that machine. - Dropped the hand-maintained 'Skills' registry. It listed four skills by absolute path under /Users/clement/.codex/skills and /Users/clement/Documents, none of which resolve here, plus 25 lines instructing the agent how to discover and load skills. Skill discovery is the harness's job and differs per harness; a checked-in registry of one engineer's local paths is exactly the rot-prone state STD-001 forbids. If those skills are worth sharing they belong in the repo under .agents/skills/ (STD-003), not as pointers into a home directory. Verified with automation/scripts/check-agents-md.sh. AGENTS.md is 298 lines, over the 200-line content rule. Not addressed here: splitting it (storage-layout checks and the Foundry/Talos mandatory check are the candidates for .claude/rules/) changes what every agent in this repo reads and should be its own reviewed change.
sparrowDom
left a comment
There was a problem hiding this comment.
Just 1 comment inline about the deploy files not written in foundry
| @@ -0,0 +1,37 @@ | |||
| const { deployOnBase } = require("../../utils/deploy-l2"); | |||
There was a problem hiding this comment.
Shouldn't these deploys be written in foundry?
naddison36
left a comment
There was a problem hiding this comment.
Can you change the deploy scripts to use Foundry
The deploy-time gate keys on the contract name, so BridgedWOETHMigrationStrategy had no descriptor on Base and passed as a brand-new contract — the one upgrade in this stack that touches a live proxy ran without a layout comparison. Seed deployments/base/BridgedWOETHMigrationStrategy.json with the live BridgedWOETHStrategy storage layout so check-storage-upgrade.js compares the new implementation against what the proxy runs today. Verified both ways: the gate passes on the real layout (totalBridged/maxPerBridge appended after V1's slot-157 tail) and exits 1 with a layoutchange finding if a V1 slot moves. The post-deploy descriptor refresh replaces the seed wholesale.
Master gained 005_DeployOETHVaultLens.s.sol (#2953) after this branch diverged, so the OETHb V3 mainnet scripts would collide with it on merge. Bump file names, contract names and constructor deployment IDs, and update the cross-references in base/002, base/003, the RemoteWOTokenStrategy fork test comment, DESIGN.md and README.md. Neither script has been broadcast, so no deployment state is affected.
b9d72a7 to
6c47c1b
Compare
sparrowDom
left a comment
There was a problem hiding this comment.
One minor thing to resolve below. I've also pushed 2 commits:
- renamed (bumped number) deploy files to reflect the situation on master
- the Bridged strategy is a migration one and replaces the existing one. If we just copy the previous strategy storage slot but name it as the new strategy json the storage slot check will still run.
There was a problem hiding this comment.
So this Claude file just simlinks to Agents. Master has a newer Claude.md file can you merge those things please?
|
Also CI is still not green. |
Summary
OETHb Phase 1 cross-chain strategy pair (Master on Base / Remote on Ethereum) with a bridge-agnostic adapter family (CCIP, Superbridge). Migrates the Base vault's wOETH position off the oracle-priced BridgedWOETHStrategy so it earns real, reported yield.
Changes