Repository navigation
feat: stock migration and bridge withdraw modules, TopUpDest wrapStock, Backed CCIP adapter - #331
nikkaroraa wants to merge 42 commits into
Conversation
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>
…k, Backed CCIP adapter
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
Certora Run Started (Certora verification)
Certora Run Summary
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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.
There was a problem hiding this comment.
Verification Results
- Group ID: 40916feb-a375-498d-9a14-9a30629e2bca
- Commit: 6d9b718
| Job | Result | VERIFIED |
|---|---|---|
| CashModuleCore.conf --rule "p03" | ✅ | 2 |
Certora Run Started (Certora verification)
Certora Run Summary
|
There was a problem hiding this comment.
Verification Results
- Group ID: 7abbd119-bd2f-4ff3-8379-903469dd18c3
- Commit: b6ef260
| Job | Result | VERIFIED |
|---|---|---|
| CashModuleCore.conf --rule "p03" | ✅ | 2 |
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Verification Results
- Group ID: 400151a2-9e43-400a-b0a9-192e3a8b2264
- Commit: f862956
| Job | Result | VERIFIED |
|---|---|---|
| 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
There was a problem hiding this comment.
Verification Results
- Group ID: a8b9cbcb-136f-4392-b685-cc58a143376f
- Commit: 0d1a7ab
| Job | Result | VERIFIED |
|---|---|---|
| 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.
There was a problem hiding this comment.
Verification Results
- Group ID: 1abf2667-2040-4709-ac07-43fd1219b195
- Commit: dceaffa
| Job | Result | VERIFIED |
|---|---|---|
| EtherFiSafe.… | ⌛ | 0 |
| CashModuleCo… | ✅ | 2 |
…ik/xstocks-op-contracts
Certora Run Started (Certora verification)
Certora Run Summary
|
There was a problem hiding this comment.
Verification Results
- Group ID: 15728cf8-1f22-475e-b7c8-f0d062870c40
- Commit: 364e467
| Job | Result | VERIFIED |
|---|---|---|
| EtherFiSafe.… | ✅ | 14 |
| CashModuleCo… | ✅ | 2 |
…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>
merge regating logic
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
8e5e7a8 to
a8e75c2
Compare
…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>
…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>
There was a problem hiding this comment.
Verification Results
- Group ID: 255629a1-4b32-4867-8d65-34f9bede0283
- Commit: 933392c
| Job | Result | VERIFIED |
|---|---|---|
| EtherFiSafe.… | ✅ | 14 |
| CashModuleCo… | ✅ | 2 |
…loyments chore: add the xStocks OP prod deployments, deploy scripts and 3CP-730 bundles
There was a problem hiding this comment.
Verification Results
- Group ID: 133dfead-857a-4926-aa5b-a195310663b7
- Commit: c8eb6d9
| Job | Result | VERIFIED |
|---|---|---|
| EtherFiSafe.… | ✅ | 14 |
| CashModuleCo… | ✅ | 2 |

Summary
Native OP stock rails for SPYx, QQQx and TBLLx, plus the fixes for Certora's draft report (Item 24, commit 8e0dab3).
Certora draft fixes, one commit:
Closes COR-1773, COR-1781, COR-1774, COR-1639, COR-1640
Evidence
test_migrate_optedOutSafeWithDebt_swapsLooseAndDefersSuppliedreverts withHasOpenBorrowsfrom the CashModule.After: loose balance swapped, supplied leg and debt untouched,
SuppliedDeferredemitted. NewOptOutBlockedcase covered.FOUNDRY_PROFILE=lend TEST_CHAIN=10 TEST_RPC="$OPTIMISM_RPC").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), andStockBridgeWithdrawModule(signed wrapper withdrawal, redeem, CCIP to Ethereum).TopUpDestgainssetStockWrappers/wrapStockand books actual received amounts on deposit for rebasing stock.BackedCCIPBridgeAdapterroutes mainnetTopUpFactoryraw-stock bridges to OPTopUpDest.ERC4626RatePriceFeedprices wrappers from liveconvertToAssets× 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,OptOutBlockedwhen only supplied remains.Tests: Unit/gateway/fork coverage for migration, wrap, bridge withdraw, adapter, price feed, and OP
TopUpDestupgrade.Reviewed by Cursor Bugbot for commit c8eb6d9. Bugbot is set up for automated code reviews on this repo. Configure here.