feat: arbitrary asset support on ethereum#124
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6917f88cc7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Certora Run Started (Certora verification)
Certora Run Summary
|
Certora Run Started (Certora verification)
Certora Run Summary
|
…into feat/eth-asset-support
…o feat/eth-asset-support
Mirrors AcrossSwapModule's request/execute lifecycle but forwards BE-supplied Enso router calldata, enabling same-chain asset swaps that Across cannot serve. Includes deploy script, trading-account wiring (OP + mainnet), and full test coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
feat: add EnsoSwapModule for same-chain + cross-chain swaps
Once a stored swap passes its `order.deadline`, `executeSwap` can never succeed again (it reverts `OrderExpired`), yet the swap and its CashModule solvency hold sit locked until an owner signs `cancelSwap`. This blocks the one-active-order-per-safe slot and leaves the withdrawal hold in place, forcing the user to sign a cancel just to recover. Add `cancelExpiredSwap(address safe)` to both EnsoSwapModule and AcrossSwapModule (covers all trading order types: buy/sell/swap). It takes no signature and can be called by anyone (e.g. the backend keeper) once `block.timestamp > order.deadline`, mirroring `cancelSwap`'s clearing path (`cancelWithdrawalByModule`, or direct delete when `cashModule == 0`). Safe by construction: it can only run after expiry (when execution is already impossible) and merely refunds the safe's own funds back to the safe and clears state — there is no fund-movement authority to abuse. Reverts `OrderNotExpired` at/before the deadline and `NoActiveOrder` when nothing is stored. Adds forge coverage for both modules. Co-authored-by: Cursor <cursoragent@cursor.com>
…red-swap feat: permissionless cancelExpiredSwap for stuck trading orders
Remove the undeployed LayerZero ownership-sync system so Optimism EtherFiSafes and mainnet TradingSafes manage owners and recovery independently on their own chains. - Delete OwnershipBridgeSender/Receiver, OwnerBridgePublisher, TradingOwnerBridgeReceiver, OwnershipBridgeMessageLib, their interfaces, the wire script, and bridge tests. - Drop the _publish* hooks from EtherFiSafeBase/MultiSig/RecoveryManager and simplify EtherFiSafe/TradingSafe to EtherFiSafeCore. - Make configureOwners/setThreshold/recoverSafe/cancelRecovery nonpayable (no LZ fee), which also removes the trapped-ETH path (L-04). - Remove ownershipBridgeSender storage/API from EtherFiDataProvider. - Update trading-account deploy scripts, dev deployment JSON, and trading-safe tests for the chain-local model. Eliminates audit findings M-02, M-03, M-04, M-07, L-04, L-07 and the bridge-sender informationals I-04/I-05/I-06. 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>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…-ownership fix: address Certora trading account audit findings
Signed-off-by: Jacob T Firek <106350168+jtfirek@users.noreply.github.com>
| "BeHYPEStakeModule": "0x6477daec9a81AC76A7276e3f62136E0A10ca7A68", | ||
| "AssetRecoveryModule": "0xD0134F725b025723f9B5C6acDD0fF8BAacC5E2c6", | ||
| "MidasModule": "0x292B353d262E00a215E096A97596b55F8AFb00Df", | ||
| "AcrossSwapModule": "0x2D749A1A0cdB663780b16b401d55e9D31FE85C7b" |
There was a problem hiding this comment.
Broken deployments JSON syntax
High Severity
The new AcrossSwapModule entry is missing a trailing comma, so the file is invalid JSON. Scripts and tooling that call stdJson.readAddress / vm.readFile on this deployment artifact will fail to parse it. MidasModule is also duplicated.
Reviewed by Cursor Bugbot for commit e5d41c5. Configure here.
Signed-off-by: Jacob T Firek <106350168+jtfirek@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a42d1cc. Configure here.
| to[2] = swap.order.srcToken; | ||
| data[2] = abi.encodeCall(IERC20.approve, (ensoRouter, 0)); | ||
|
|
||
| IEtherFiSafe(safe).execTransactionFromModule(to, values, data); |
There was a problem hiding this comment.
Native ETH source swaps fail
Medium Severity
_dispatchSwap always approves order.srcToken and sends value = 0 to the Enso router. Native ETH (0xEee…) is allowed by validation and handled as an output, but as an input the approve targets a non-token and no ETH is forwarded, so ETH→token swaps cannot execute.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a42d1cc. Configure here.
There was a problem hiding this comment.
Verification Results
- Group ID: 2a0a2c42-b10e-4b02-bb11-23bb835feaef
- Commit: a42d1cc
| Job | Result | VERIFIED |
|---|---|---|
| EtherFiSafe.… | ✅ | 14 |
| CashModuleCo… | ✅ | 2 |


Note
High Risk
Large new surface for user-signed swaps, cross-chain bridging, and role-gated fund redirects between TopUp and TradingSafe; mistakes in signature binding, hold timing, or redirect guards could move or strand user assets.
Overview
Adds a mainnet trading-account stack so users can hold and trade assets on Ethereum that are not supported on the OP cash rail, wired to the existing OP TopUp flow.
Trading safes & lens: Introduces
TradingSafe/TradingSafeFactory(deterministic addresses from the source TopUp/safe identity) andTradingLensfor supported trading tokens and balance/USD reads. Shared safe logic moves intoEtherFiSafeCore; OPEtherFiSafebecomes a thin wrapper. Factory deploy init now targetsEtherFiSafeCore.initialize;EtherFiSafeFactory.reinitializeis removed.Swap modules: New
AcrossSwapModule(signed request → optional CashModule withdrawal hold →executeSwapreplays AcrossdepositV3or origin periphery calldata) andEnsoSwapModule(same lifecycle via pinned Enso router calldata; same-chainminOutbalance check). On mainnet trading DataProvider (cashModule == 0), swaps run immediately at request; on OP they use the hold/delay path.TopUp ↔ TradingSafe recovery:
TopUpFactorygainssetTradingSafeFactory,redirectToTradingSafe/ batch (role-gated; trading-supported, not topup-supported tokens).TradingSafeFactorymirrors withredirectToTopUpfor topup-supported assets, usingTradingLens/ TopUp factory oracles.Deploy & ops:
EtherFiDeployer(CREATE3), scripts for bootstrap, full mainnet trading deploy (atomic proxy init, address prediction), OP module rollout, standalone Enso deploy; dev deployment JSONs and mainnet fixture updates. Certora patches switchEnumerableSetto OpenZpelin onEtherFiSafeCore/ factory.Reviewed by Cursor Bugbot for commit a42d1cc. Bugbot is set up for automated code reviews on this repo. Configure here.