Skip to content

feat: stock migration and bridge withdraw modules, TopUpDest wrapStock, Backed CCIP adapter - #331

Open
nikkaroraa wants to merge 42 commits into
masterfrom
nik/xstocks-op-contracts
Open

nikkaroraa wants to merge 42 commits into
masterfrom
nik/xstocks-op-contracts

Conversation

@nikkaroraa

@nikkaroraa nikkaroraa commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Native OP stock rails for SPYx, QQQx and TBLLx, plus the fixes for Certora's draft report (Item 24, commit 8e0dab3).

StockMigrationModule   keeper swaps a safe's stand-in 1:1 for Backed's OP wrapper, loose and Aave legs
StockWrapModule        wraps raw stock sent to a safe into its wrapper
StockBridgeWithdrawModule  signed wrapper withdrawal, redeem, raw stock to Ethereum over Backed's CCIP bridge
TopUpDest              setStockWrappers + wrapStock turn bridged raw stock into wrapper float
BackedCCIPBridgeAdapter    TopUpFactory sends raw stock over the same bridge
ERC4626RatePriceFeed   wrapper price = live convertToAssets x underlying USD feed

Certora draft fixes, one commit:

 StockMigrationModule._migrate
-  processLendOptOut(safe)                 reverts on open borrows, whole safe skipped
+  try processLendOptOut(safe) catch       emit SuppliedDeferred, swap the loose balance only
+  revert OptOutBlocked when nothing loose

 StockWrapModule._wrap / TopUpDest.wrapStock
+  approve(wrapper, 0) after the deposit

 TopUpDest.deposit / wrapStock
-  book the requested amount
+  book the measured balance change (rebasing stock delivers whole shares)

 ERC4626RatePriceFeed, adapter, withdraw module
+  notes: issuer-trusted rate, sub-share dust stays in the sending contract

Closes COR-1773, COR-1781, COR-1774, COR-1639, COR-1640

Evidence

  • Before: test_migrate_optedOutSafeWithDebt_swapsLooseAndDefersSupplied reverts with HasOpenBorrows from the CashModule.
    After: loose balance swapped, supplied leg and debt untouched, SuppliedDeferred emitted. New OptOutBlocked case covered.
  • StockMigrationModuleGateway 19 passed (FOUNDRY_PROFILE=lend TEST_CHAIN=10 TEST_RPC="$OPTIMISM_RPC").
  • TopUpDest, TopUpDest OP upgrade fork, StockWrapModule, StockBridgeWithdrawModule (real send on an OP fork), BackedCCIPBridgeAdapter mainnet fork, ERC4626RatePriceFeed: 73 passed.

Merge Danger

Door: two-way

New modules and a TopUpDest upgrade behind the OP timelocks; nothing is enabled by merging. The launch bundle whitelists only the wrappers as CashModule withdraw assets, not the raw stocks.

Blast Radius: stocks

Collateral migration, cross-chain stock withdrawals and the Ethereum top-up rail for the three stocks. Other assets and the swap modules' behaviour are unchanged by this PR.


Note

High Risk
Touches stock collateral migration, cross-chain CCIP bridging, and a TopUpDest upgrade; mis-timed governance or config could affect pricing, withdrawals, and top-up routing for the three xStocks.

Overview
Adds native Optimism rails for SPYx, QQQx, and TBLLx: Backed ERC-4626 wrappers replace LayerZero stand-ins for collateral, pricing, withdrawals, and top-ups.

On-chain: New StockMigrationModule (1:1 stand-in → wrapper, including Aave positions), StockWrapModule (raw → wrapper in safes), and StockBridgeWithdrawModule (signed wrapper withdrawal, redeem, CCIP to Ethereum). TopUpDest gains setStockWrappers / wrapStock and books actual received amounts on deposit for rebasing stock. BackedCCIPBridgeAdapter routes mainnet TopUpFactory raw-stock bridges to OP TopUpDest. ERC4626RatePriceFeed prices wrappers from live convertToAssets × existing USD leg.

Ops: Forge deploy/launch scripts under scripts/xstocks-op/, updated deployment manifests, and Gnosis Safe JSON bundles (8h admin + 2d upgrade timelocks on OP; Ethereum top-up token config). Certora-related tweaks: deferred supplied leg when lend opt-out is blocked, post-deposit approve clears, OptOutBlocked when only supplied remains.

Tests: Unit/gateway/fork coverage for migration, wrap, bridge withdraw, adapter, price feed, and OP TopUpDest upgrade.

Reviewed by Cursor Bugbot for commit c8eb6d9. Bugbot is set up for automated code reviews on this repo. Configure here.

jtfirek and others added 14 commits September 18, 2026 12:42
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- CashModule: per-module withdrawal delay override (configureModuleWithdrawalDelay,
  getWithdrawalDelayForModule). User requestWithdrawal keeps the global delay.
- ModuleLendGatewaySandwich: _requiresSolvencyHold classifies collateral and card
  spend assets (gateway LTV / isSpendAsset, legacy DebtManager collateral / borrow).
- Enso/Across: only collateral or spend-asset inputs place a CashModule hold, using
  the module's delay; any other input executes in the request tx.

Co-authored-by: Cursor <cursoragent@cursor.com>
A gateway safe's collateral input is held only while the safe is in Credit mode
or carries any debt. Spend assets stay always-held; legacy safes keep holding
collateral since the no-hold path runs no DebtManager health check.

Co-authored-by: Cursor <cursoragent@cursor.com>
CashLens authorizes card spends in the incoming mode before it matures, so a
pending switch to Credit must keep collateral trades behind the hold.

Co-authored-by: Cursor <cursoragent@cursor.com>
Skip the hold only for a Debit-mode safe with no debt and no pending mode
switch, dropping the incoming-mode timing checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Only a mode switch still inside its delay forces the hold; afterwards use
getMode, which applies a matured switch that storage has not recorded yet.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@github-actions

Copy link
Copy Markdown

Certora Run Started (Certora verification)

  • Group ID: 40916feb-a375-498d-9a14-9a30629e2bca
Config Status Link Log File
CashModuleCore.conf --rule "p03" Submitted link certora/confs/CashModuleCore.conf-bd22eca6fc8e.log
EtherFiSafe.conf Failed (1) - certora/confs/EtherFiSafe.conf-d01ff53d25c3.log

Certora Run Summary

  • Started 1 jobs
  • 1 jobs failed

Download Logs

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6d9b718. Configure here.

Comment thread src/top-up/TopUpDest.sol

@certora-run certora-run Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 40916feb-a375-498d-9a14-9a30629e2bca
  • Commit: 6d9b718
JobResultVERIFIED
CashModuleCore.conf --rule "p03"✅2

@github-actions

Copy link
Copy Markdown

Certora Run Started (Certora verification)

  • Group ID: 7abbd119-bd2f-4ff3-8379-903469dd18c3
Config Status Link Log File
CashModuleCore.conf --rule "p03" Submitted link certora/confs/CashModuleCore.conf-ff9b6583bb81.log
EtherFiSafe.conf Failed (1) - certora/confs/EtherFiSafe.conf-ecf787deaee8.log

Certora Run Summary

  • Started 1 jobs
  • 1 jobs failed

Download Logs

@certora-run certora-run Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 7abbd119-bd2f-4ff3-8379-903469dd18c3
  • Commit: b6ef260
JobResultVERIFIED
CashModuleCore.conf --rule "p03"✅2

@certora-run certora-run Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 400151a2-9e43-400a-b0a9-192e3a8b2264
  • Commit: f862956
JobResultVERIFIED
EtherFiSafe.…✅14
CashModuleCo…✅2

…dgers and returns

- StockBridgeWithdrawModule: executeWithdrawal takes the fee from msg.value and
  refunds the excess; the native float, withdrawNative and receive() are gone
- events renamed to BridgeWithdrawal* so they no longer collide with the OFT module
- TopUpDest.wrapStock releases raw that was booked through deposit()
- drop two unused IBackedCCIPBridge functions, unnamed returns, comment wording
- tests: fee paid by the caller and refunded, raw ledger release, legacy safe
  with debt migrates only when the wrapper is legacy collateral

@certora-run certora-run Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: a8b9cbcb-136f-4392-b685-cc58a143376f
  • Commit: 0d1a7ab
JobResultVERIFIED
EtherFiSafe.…✅14
CashModuleCo…✅2

…longer names the module

Both cancel paths asked the CashModule to release the hold and relied on its callback to delete the stored order. The CashModule skips that callback once the module is off its whitelist, so an order released in that window stayed stored and blocked every later request for the safe. Cancel now checks whether the hold still names this module and clears the order itself otherwise, matching the other bridge modules.

@certora-run certora-run Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 1abf2667-2040-4709-ac07-43fd1219b195
  • Commit: dceaffa
JobResultVERIFIED
EtherFiSafe.…⌛0
CashModuleCo…✅2

@nikkaroraa
nikkaroraa changed the base branch from master to jacob/trade-audit-bundle October 5, 2026 10:39
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Certora Run Started (Certora verification)

  • Group ID: b6e140b1-6e0d-438c-a5c0-4b222608b553
Config Status Link Log File
CashModuleCore.conf --rule "p03" Failed (1) - certora/confs/CashModuleCore.conf-057012fd6d29.log
EtherFiSafe.conf Failed (1) - certora/confs/EtherFiSafe.conf-3e6503a4f3d2.log

Certora Run Summary

  • Started 0 jobs
  • 2 jobs failed

Download Logs

@certora-run certora-run Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 15728cf8-1f22-475e-b7c8-f0d062870c40
  • Commit: 364e467
JobResultVERIFIED
EtherFiSafe.…✅14
CashModuleCo…✅2

jtfirek and others added 12 commits October 5, 2026 13:06
…tora L-01)

A callback during the Across/Enso external call could place a CashModule
hold on tokens the integration then pulls, leaving pendingWithdrawal >
balance. Check every pending-withdrawal amount is still held once control
returns, in _dispatchOriginSwap, _dispatchDeposit and _dispatchSwap (after
the Enso resupply).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…onsistency

fix: cover OpenOcean holds and caller-aware module delays
…check

fix(swap): require pending withdrawal backed after external call (Certora L-01)
Signed-off-by: Jacob T Firek <106350168+jtfirek@users.noreply.github.com>
…ity.pdf

Signed-off-by: Jacob T Firek <106350168+jtfirek@users.noreply.github.com>
master removed CASH_MODULE_CONTROLLER_ROLE; use the same gate as
configureModulesCanRequestWithdraw. Also pass the TradingSafeFactory
to the Enso/Across constructors in the mainnet bytecode checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n-timelock

Gate configureModuleWithdrawalDelay behind ADMIN_TIMELOCK_ROLE
@shivam-ef
shivam-ef force-pushed the jacob/trade-audit-bundle branch from 8e5e7a8 to a8e75c2 Compare October 19, 2026 09:07
shivam-ef and others added 2 commits October 19, 2026 16:30
…racts

# Conflicts:
#	test/across/AcrossSwapModule.t.sol
#	test/enso/EnsoSwapModule.t.sol
Master already has this report as audit/ether.fi <> Certora - trading security.pdf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shivam-ef
shivam-ef changed the base branch from jacob/trade-audit-bundle to master October 19, 2026 09:37
shivam-ef
shivam-ef previously approved these changes Oct 19, 2026
…ule setUp

vm.prank applied to the ADMIN_TIMELOCK_ROLE() argument call instead of grantRole, so grantRole ran as the test contract and reverted with EnumerableRolesUnauthorized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shivam-ef
shivam-ef previously approved these changes Oct 19, 2026

@certora-run certora-run Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 255629a1-4b32-4867-8d65-34f9bede0283
  • Commit: 933392c
JobResultVERIFIED
EtherFiSafe.…✅14
CashModuleCo…✅2

…loyments

chore: add the xStocks OP prod deployments, deploy scripts and 3CP-730 bundles

@certora-run certora-run Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Verification Results

  • Group ID: 133dfead-857a-4926-aa5b-a195310663b7
  • Commit: c8eb6d9
JobResultVERIFIED
EtherFiSafe.…✅14
CashModuleCo…✅2

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