Skip to content

Add OETHb migration contracts - #2909

Open
shahthepro wants to merge 73 commits into
masterfrom
shah/ousd-v3
Open

Add OETHb migration contracts#2909
shahthepro wants to merge 73 commits into
masterfrom
shah/ousd-v3

Conversation

@shahthepro

@shahthepro shahthepro commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

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

  • Master + Remote strategies on a single nonce-gated yield channel — one operation in flight at a time (deposit / withdraw / claim), plus a balance report Remote pushes on an operator cadence.
  • Adapters (CCIPAdapter, SuperbridgeAdapter) on a shared AbstractAdapter base — multi-tenant whitelist, per-lane config, governor-settable min/maxTransferAmount caps.
  • CreateX/CREATE2 proxies (BridgeAdapterProxy, CrossChainStrategyProxy) so paired chains share addresses (required for the transportSender == address(this) peer-parity check).
  • BridgedWOETHMigrationStrategy — upgrade impl for the existing Base proxy, adds bridgeToRemote() to CCIP-ship the custodied wOETH in rate-limited batches.
  • Only the backing asset (WETH) crosses a bridge; the OToken and wOToken never do. Both tokens must be 18-decimal, asserted at construction.
  • Production OETHb deploys (deploy/base/100-104_*, deploy/mainnet/210-211_*). Base→Ethereum destGasLimit sized to 2M against measured worst-case inbound gas, with a Foundry fork-test regression.
  • README.md, DESIGN.md, FLOWS.md (sequence diagrams) + sol2uml diagrams.

@shahthepro shahthepro changed the title [WIP] Add OUSD V3 contracts Add OUSD V3 and OETHb migration contracts Jun 8, 2026
@shahthepro
shahthepro marked this pull request as ready for review June 8, 2026 10:06
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.38945% with 148 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.57%. Comparing base (c52692d) to head (dd40635).

Files with missing lines Patch % Lines
...racts/strategies/BridgedWOETHMigrationStrategy.sol 0.00% 45 Missing ⚠️
...egies/crosschainV3/adapters/SuperbridgeAdapter.sol 56.52% 30 Missing ⚠️
...s/strategies/crosschainV3/adapters/CCIPAdapter.sol 54.54% 15 Missing ⚠️
...rategies/crosschainV3/adapters/AbstractAdapter.sol 86.00% 14 Missing ⚠️
...s/strategies/crosschainV3/adapters/CCTPAdapter.sol 88.29% 11 Missing ⚠️
.../strategies/crosschainV3/MasterWOTokenStrategy.sol 95.42% 8 Missing ⚠️
contracts/contracts/utils/BytesHelper.sol 0.00% 8 Missing ⚠️
...gies/crosschainV3/AbstractCrossChainV3Strategy.sol 87.93% 7 Missing ⚠️
...trategies/crosschainV3/AbstractWOTokenStrategy.sol 94.31% 5 Missing ⚠️
.../strategies/crosschainV3/RemoteWOTokenStrategy.sol 97.10% 5 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@clement-ux clement-ux left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Left some comments

Comment thread contracts/contracts/strategies/crosschainV3/adapters/CCTPAdapter.sol Outdated
sparrowDom
sparrowDom previously approved these changes Jul 7, 2026

@sparrowDom sparrowDom left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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).

shahthepro added 12 commits July 7, 2026 14:43
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.
@shahthepro shahthepro changed the title Add OUSD V3 and OETHb migration contracts Add OETHb migration contracts Aug 25, 2026

@sparrowDom sparrowDom left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just 1 comment inline about the deploy files not written in foundry

@@ -0,0 +1,37 @@
const { deployOnBase } = require("../../utils/deploy-l2");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't these deploys be written in foundry?

@naddison36 naddison36 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you change the deploy scripts to use Foundry

shahthepro and others added 3 commits August 28, 2026 14:46
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.

@sparrowDom sparrowDom left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread CLAUDE.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So this Claude file just simlinks to Agents. Master has a newer Claude.md file can you merge those things please?

@sparrowDom

Copy link
Copy Markdown
Member

Also CI is still not green.

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.

4 participants