Skip to content

Introduce Omnibridge helper#1

Closed
fedgiac wants to merge 7 commits intomainfrom
approve-and-bridge-draft
Closed

Introduce Omnibridge helper#1
fedgiac wants to merge 7 commits intomainfrom
approve-and-bridge-draft

Conversation

@fedgiac
Copy link
Collaborator

@fedgiac fedgiac commented Jun 12, 2025

A first draft for a helper contract for the swap and bridge feature.

It was written so that it requires minimum effort to migrate the Omnibridge code from the SDK away from Weiroll in favor of OmnibridgeApproveAndBridge.

It's a proof of concept and not intended for production use at this point.

Notably, it solves two issues we discussed in the smart-contract team:

  • With this, Weiroll shouldn't be needed anymore.
  • The balance of the shed is checked against a minimum reference, meaning that the hook cannot be executed if the order hasn't been executed (or an attacker sent funds to the shed).

How to test/understand

See the forked e2e test on mainnet.

Summary by CodeRabbit

  • New Features
    • Introduced a helper contract for swap-and-bridge functionality via CoW Protocol, including integration with Omnibridge for token bridging.
    • Added interfaces and libraries for ERC-20 tokens and safe token operations.
    • Provided deployment scripts and configuration for streamlined development and testing.
  • Documentation
    • Rewrote the README to reflect the new proof-of-concept contract and updated usage instructions.
    • Added the full GNU GPLv3 license.
  • Tests
    • Added comprehensive unit and end-to-end tests for the new bridging contract.
  • Chores
    • Updated project configuration for Solidity version, formatting, and CI.
  • Refactor
    • Removed legacy Counter contract, scripts, and related tests.

@fedgiac fedgiac requested a review from anxolin June 12, 2025 15:29
@anxolin
Copy link
Collaborator

anxolin commented Jun 12, 2025

@coderabbitia review

@coderabbitai
Copy link

coderabbitai bot commented Jun 12, 2025

You are on the CodeRabbit Free Plan. In order to use the Chat feature, please upgrade to CodeRabbit Pro.

@anxolin
Copy link
Collaborator

anxolin commented Jun 12, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jun 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Jun 12, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update restructures the project from a basic Foundry template to a specialized proof-of-concept for a swap-and-bridge helper contract using CoW Protocol and Omnibridge. It introduces new Solidity contracts, interfaces, tests, and configuration, while removing the original Counter example and its related scripts and tests. Documentation and licensing are updated accordingly.

Changes

File(s) Change Summary
LICENSE Added full GNU GPLv3 license text.
README.md Rewritten to describe the new swap-and-bridge helper contract, usage, and development instructions.
foundry.toml Overhauled with explicit compiler, test, formatting, and CI settings.
script/Counter.s.sol, src/Counter.sol, test/Counter.t.sol Removed Counter example contract, deployment script, and tests.
script/Deploy.s.sol Added deployment script for OmnibridgeApproveAndBridge contract.
src/OmnibridgeApproveAndBridge.sol Added new contract for Omnibridge bridging, inheriting from a mixin.
src/interface/IApproveAndBridge.sol Added interface for approve-and-bridge operations.
src/interface/IOmnibridge.sol Added interface for Omnibridge bridge interactions.
src/mixin/ApproveAndBridge.sol Added abstract contract implementing approval and bridging logic.
src/vendored/IERC20.sol, src/vendored/SafeERC20.sol Added vendored ERC20 interface and SafeERC20 utility library.
test/OmnibridgeApproveAndBridge.t.sol Added basic test contract for OmnibridgeApproveAndBridge setup.
test/e2e/OmnibridgeApproveAndBridge.t.sol Added end-to-end test for bridging flow on forked mainnet.
test/e2e/lib/ForkedRpc.sol Added utility library for creating mainnet forks in tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Proxy (COWShed)
    participant OmnibridgeApproveAndBridge
    participant Omnibridge

    User->>Proxy: Transfer tokens (e.g., WETH)
    User->>Proxy: Delegatecall approveAndBridge(token, minAmount, receiver)
    Proxy->>OmnibridgeApproveAndBridge: approveAndBridge(token, minAmount, receiver)
    OmnibridgeApproveAndBridge->>token: forceApprove(Omnibridge, amount)
    OmnibridgeApproveAndBridge->>Omnibridge: relayTokens(token, receiver, amount)
    Omnibridge-->>User: TokensBridgingInitiated event
Loading

Poem

In fields of code where bridges grow,
A rabbit hops, with tokens in tow.
Approve, then leap across the chain,
SafeERC20 guards the lane.
Tests now fork the mainnet stream—
Bridging dreams, a bunny’s gleam!
🐇✨


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (15)
LICENSE (1)

8-675: Assess copyleft implications.
The full GPLv3 text is properly included, but GPLv3’s strong copyleft may impose downstream distribution obligations on all recipients of this proof-of-concept. For a helper/library contract intended for broader adoption, consider whether a more permissive license (e.g., LGPL or MIT) would better align with your usage and integration goals.

test/e2e/lib/ForkedRpc.sol (1)

8-15: Surface the fallback RPC choice or make it configurable

Silently defaulting to https://eth.merkle.io may:

  1. Hide mis-configuration (a typo in MAINNET_ARCHIVE_NODE_URL still falls back).
  2. Push load to a third-party endpoint that may rate-limit tests.

Consider reverting when the env var is unset or, at minimum, emitting a Forge log so it’s obvious which RPC is in use.

-        } catch {
-            forkUrl = "https://eth.merkle.io";
+        } catch {
+            // Revert early to make mis-configuration explicit.
+            revert("MAINNET_ARCHIVE_NODE_URL not set");
         }
src/interface/IOmnibridge.sol (1)

2-2: Same pragma concern as above

Pin a minor compiler version for determinism.

-pragma solidity ^0.8;
+pragma solidity ^0.8.24;
src/interface/IApproveAndBridge.sol (1)

2-2: Pin compiler minor version

See comment in ForkedRpc.sol.

-pragma solidity ^0.8;
+pragma solidity ^0.8.24;
script/Deploy.s.sol (2)

4-4: Remove unused import

console isn’t referenced. Dropping it avoids an unnecessary byte-code inclusion once solidity inlines libraries.

-import {Script, console} from "forge-std/Script.sol";
+import {Script} from "forge-std/Script.sol";

14-18: Broadcast only when required & handle non-mainnet runs

If this script is executed against a fork (chain id ≠ 1) nothing happens, which can be confusing in CI.
Two lightweight improvements:

  1. Add an else branch with a Forge log so users know the deployment was skipped.
  2. Wrap deployment between vm.startBroadcast() / vm.stopBroadcast() for multi-tx future extensions.
-        if (block.chainid == 1) {
-            vm.broadcast();
-            omnibridgeApproveAndBridge = new OmnibridgeApproveAndBridge(MAINNET_OMNIBRIDGE);
-        }
+        if (block.chainid == 1) {
+            vm.startBroadcast();
+            omnibridgeApproveAndBridge = new OmnibridgeApproveAndBridge(MAINNET_OMNIBRIDGE);
+            vm.stopBroadcast();
+        } else {
+            console2.log("DeployScript: skipped – not on mainnet");
+        }
foundry.toml (1)

11-12: 1 000 000 optimizer runs → extremely slow builds

Runs in the seven-digit range barely change gas compared with 200k but explode compile time (and CI minutes).
Unless you have measured gas savings that justify it, consider reducing to e.g. 200_000.

README.md (1)

40-44: Deployment command lacks broadcast / verify flags

Most teams eventually turn the PoC into prod and will ask “why didn’t forge script … verify / broadcast?”.
Adding --broadcast and --verify examples (commented-out is fine) will save future friction.

src/mixin/ApproveAndBridge.sol (1)

14-16: Error message could expose internal logic

"Bridging less than min amount" gives an attacker a hint that the contract keeps balance > min.
For production consider a generic message like "Insufficient balance".

test/OmnibridgeApproveAndBridge.t.sol (2)

8-15: Variable name counter is a leftover from old example

OmnibridgeApproveAndBridge public counter;

The helper is not a counter. Rename to something descriptive (e.g. bridgeHelper) to avoid confusion in future assertions.

-    OmnibridgeApproveAndBridge public counter;
+    OmnibridgeApproveAndBridge public bridgeHelper;

12-15: No actual tests yet – CI will silently succeed

setUp() is fine, but without function testSomething() external { … } forge marks the suite as passed even if nothing is asserted.
Add at least one assertion (e.g. verifying bridgeApprovalTarget() returns the mocked address) to prevent false confidence.

test/e2e/OmnibridgeApproveAndBridge.t.sol (2)

86-96: Event check misses the success path in the notEnoughFunds test

In the revert-then-top-up flow you call trustedExecuteHooks a second
time (line 132) but do not assert that the expected
TokensBridgingInitiated event is eventually emitted. Adding a second
expectEmit makes the negative & positive cases symmetric and prevents
false positives where the hook silently does nothing after the top-up.


115-133: Duplicate setup code – extract a helper

notEnoughFunds() repeats ~20 lines already present in test_happyPath
(set-up fork, deploy shed, allocate calls). Extracting a private
_prepareShed() function keeps both tests short and focused on the
behaviour they’re validating.

src/vendored/SafeERC20.sol (2)

39-46: forceApprove swallows a failing first call but not a second – consider bubbling

_callOptionalReturnBool converts any revert into false, so
forceApprove gracefully retries with approve(0).
However, the second & third low-level calls use _callOptionalReturn
which reverts on failure, bubbling the original reason. That’s fine for
most tokens but breaks the “always succeed” contract the wrapper tries
to provide. Consider wrapping the second call in the same
_callOptionalReturnBool pattern or documenting that an unexpected
second failure will bubble.


59-69: Memory clobbering risk when reading returnValue

Both low-level helpers write call return data directly to memory slot 0
and immediately read it with mload(0).
While this mirrors OZ’s implementation, it also overwrites whatever was
stored at address 0, which may break contracts relying on that memory
(for example, inline assembly utilities that reuse slot 0 as scratch).

A minimal cost improvement is to switch to the free-memory pointer:

-            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
+            let free := mload(0x40)
+            let success := call(gas(), token, 0, add(data, 0x20), mload(data), free, 0x20)
...
-            returnValue := mload(0)
+            returnValue := mload(free)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbb8f6b and 8965fe4.

📒 Files selected for processing (16)
  • LICENSE (1 hunks)
  • README.md (1 hunks)
  • foundry.toml (1 hunks)
  • script/Counter.s.sol (0 hunks)
  • script/Deploy.s.sol (1 hunks)
  • src/Counter.sol (0 hunks)
  • src/OmnibridgeApproveAndBridge.sol (1 hunks)
  • src/interface/IApproveAndBridge.sol (1 hunks)
  • src/interface/IOmnibridge.sol (1 hunks)
  • src/mixin/ApproveAndBridge.sol (1 hunks)
  • src/vendored/IERC20.sol (1 hunks)
  • src/vendored/SafeERC20.sol (1 hunks)
  • test/Counter.t.sol (0 hunks)
  • test/OmnibridgeApproveAndBridge.t.sol (1 hunks)
  • test/e2e/OmnibridgeApproveAndBridge.t.sol (1 hunks)
  • test/e2e/lib/ForkedRpc.sol (1 hunks)
💤 Files with no reviewable changes (3)
  • src/Counter.sol
  • script/Counter.s.sol
  • test/Counter.t.sol
🔇 Additional comments (5)
LICENSE (1)

1-7: Approve GPLv3 license header.
The header correctly declares “GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007” and includes the necessary copyright notice.

src/vendored/IERC20.sol (1)

1-85: Vendored code looks untouched – no issues from this diff

foundry.toml (1)

21-22: isolate = true might be ignored in profile context

The isolate test flag belongs under the [profile.*] table in foundry.toml.
Keeping it at root level may be silently ignored by forge ≥ 0.2.0. Make sure it is read by moving it inside [profile.default] or [test].

README.md (1)

1-14: Great to see “proof-of-concept” caveats up-front

Clear disclaimers reduce the chance that someone ships the PoC to production inadvertently.

src/vendored/SafeERC20.sol (1)

71-73: ⚠️ Potential issue

address(token).code.length == 0 silently allows EOAs

If the token is accidentally an EOA, _callOptionalReturn silently
accepts
a zero-length return with the EOA check passing (== 0). This
masks an obvious bug. The original OZ code treats code.length == 0
call as failure. The condition should be != 0:

- if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1)
+ if (returnSize == 0 ? address(token).code.length != 0 : returnValue != 1)

Otherwise, a typo in the token address could go unnoticed.

Likely an incorrect or invalid review comment.

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Lock-in a concrete compiler minor version

pragma solidity ^0.8; floats across every 0.8.x release. A new minor release could introduce subtle behaviour changes (e.g. new defaults for overflow checks on bytes.concat, updated Yul IR) that alter byte-code or runtime semantics and break reproducible builds.
Pin to the latest audited minor used in the repo (e.g. ^0.8.24) or even an exact version if determinism is paramount.

-pragma solidity ^0.8;
+pragma solidity ^0.8.24;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pragma solidity ^0.8;
-pragma solidity ^0.8;
+pragma solidity ^0.8.24;
🤖 Prompt for AI Agents
In test/e2e/lib/ForkedRpc.sol at line 2, the pragma solidity version is set to a
floating minor version (^0.8), which can lead to inconsistent builds due to
changes in minor releases. Update the pragma statement to pin to a specific
minor version, such as ^0.8.24 or the exact version used and audited in the
repo, to ensure deterministic and reproducible builds.

Comment on lines +6 to +13
# Compiler options
solc = "0.8.30"
evm_version = "prague" # https://docs.soliditylang.org/en/latest/using-the-compiler.html#target-options
bytecode_hash = "none"
cbor_metadata = false
optimizer = true
optimizer_runs = 1000000

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Verify evm_version = "prague" is actually supported by solc 0.8.30

prague is still an upcoming hard-fork. solc 0.8.30 may not recognise it, causing compilation warnings or silently falling back to the default EVM version, which defeats the purpose of pinning.
Please double-check that the compiler in your CI image supports this tag and upgrade/pin accordingly.

🤖 Prompt for AI Agents
In foundry.toml around lines 6 to 13, the evm_version is set to "prague" which
may not be supported by solc 0.8.30, potentially causing compilation warnings or
fallback behavior. Verify if solc 0.8.30 in your CI environment supports
"prague"; if not, update the solc version to one that does or change evm_version
to a supported value to ensure consistent compilation behavior.

Comment on lines +12 to +14
constructor(IOmnibridge omnibridge_) {
omnibridge = omnibridge_;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against zero-address Omnibridge in constructor

If someone accidentally passes IOmnibridge(address(0)), approvals will later fail silently. Add an explicit check for clarity.

 constructor(IOmnibridge omnibridge_) {
-    omnibridge = omnibridge_;
+    require(address(omnibridge_) != address(0), "Zero omnibridge");
+    omnibridge = omnibridge_;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
constructor(IOmnibridge omnibridge_) {
omnibridge = omnibridge_;
}
constructor(IOmnibridge omnibridge_) {
require(address(omnibridge_) != address(0), "Zero omnibridge");
omnibridge = omnibridge_;
}
🤖 Prompt for AI Agents
In src/OmnibridgeApproveAndBridge.sol around lines 12 to 14, the constructor
assigns the omnibridge address without validation, which can lead to silent
failures if a zero address is passed. Add a require statement to check that the
omnibridge_ parameter is not the zero address before assignment, reverting the
transaction with a clear error message if it is zero.

Comment on lines +2 to +3
pragma solidity ^0.8;

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Pin pragma to the same minor as foundry.toml

pragma solidity ^0.8; picks up future compiler versions with breaking semantic-checker changes.
Since the repo pins solc = "0.8.30" it’s safer to align the pragma to ^0.8.30 (or =0.8.30) to guarantee identical byte-code across machines.

-pragma solidity ^0.8;
+pragma solidity ^0.8.30;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pragma solidity ^0.8;
pragma solidity ^0.8.30;
🤖 Prompt for AI Agents
In src/OmnibridgeApproveAndBridge.sol at lines 2 to 3, the Solidity pragma is
set to ^0.8, which allows any 0.8.x compiler version and may cause
inconsistencies. Update the pragma to ^0.8.30 or =0.8.30 to match the solc
version specified in foundry.toml, ensuring consistent compilation and bytecode
across environments.

Comment on lines +11 to +20
/// @dev This function isn't intended to be called directly, it should be
/// delegatecalled instead.
function approveAndBridge(IERC20 token, uint256 minAmount, address receiver) external {
uint256 balance = token.balanceOf(address(this));
require(balance >= minAmount, "Bridging less than min amount");

token.forceApprove(bridgeApprovalTarget(), balance);

bridge(token, balance, receiver);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential re-entrancy between forceApprove and bridge

forceApprove invokes an external call on any ERC-20; a malicious token could re-enter approveAndBridge before the bridge call executes.
Even though no mutable state is written, child contracts overriding bridge() might be vulnerable.

Mitigation: add a simple ReentrancyGuard modifier or an internal status flag.

🤖 Prompt for AI Agents
In src/mixin/ApproveAndBridge.sol around lines 11 to 20, the approveAndBridge
function calls forceApprove on an external token before calling bridge, which
can lead to re-entrancy if the token is malicious and bridge is overridden in
child contracts. To fix this, add a ReentrancyGuard modifier or implement an
internal status flag to prevent re-entrant calls during the execution of
approveAndBridge, ensuring the function cannot be re-entered before it
completes.

Comment on lines +68 to +76
uint256 orderProceeds = 1_337 ether;
uint256 minProceeds = 42 ether;
assertGt(orderProceeds, minProceeds);

// For simplicity we take the funds from the user, but they should come
// from an order.
vm.prank(user);
WETH.transfer(address(shed), orderProceeds);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Test depends on live mainnet balances – make it deterministic

The fork assumes that user still holds ≥ 1 337 WETH at block
22689062. Should that balance ever drop (e.g. a future airdrop,
migration, or account sweep), the test will revert.

Foundry provides vm.deal / deal helpers; use them to mint the exact
amount needed and keep the test hermetic:

- vm.prank(user);
- WETH.transfer(address(shed), orderProceeds);
+ deal(address(WETH), user, orderProceeds);
+ vm.prank(user);
+ WETH.transfer(address(shed), orderProceeds);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uint256 orderProceeds = 1_337 ether;
uint256 minProceeds = 42 ether;
assertGt(orderProceeds, minProceeds);
// For simplicity we take the funds from the user, but they should come
// from an order.
vm.prank(user);
WETH.transfer(address(shed), orderProceeds);
uint256 orderProceeds = 1_337 ether;
uint256 minProceeds = 42 ether;
assertGt(orderProceeds, minProceeds);
// For simplicity we take the funds from the user, but they should come
// from an order.
- vm.prank(user);
- WETH.transfer(address(shed), orderProceeds);
+ deal(address(WETH), user, orderProceeds);
+ vm.prank(user);
+ WETH.transfer(address(shed), orderProceeds);
🤖 Prompt for AI Agents
In test/e2e/OmnibridgeApproveAndBridge.t.sol around lines 68 to 76, the test
relies on the user having a live mainnet WETH balance of at least 1,337 ether,
which can cause flakiness if the balance changes. To fix this, use Foundry's
vm.deal or deal helper to set the user's WETH balance explicitly to the required
amount before the transfer, ensuring the test is deterministic and does not
depend on external state.

return address(omnibridge);
}

function bridge(IERC20 token, uint256 amount, address receiver) internal override {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I would add minReceiveAmount, because the amount to bridge will be likely bigger. This should be used just for the premature-execution assertion

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This function is a helper used by the contract that inherits ApproveAndBridge to explain how bridging works in the specific implementation (and so get the approveAndBridge function for free).
We can change the parameters if needed (to make it more generic, we could add a bytes array) but a minReceiveAmount isn't part of the bridging operation per se, it only needs to appear in approveAndBridge (where it does).


vm.forkEthereumMainnetAtBlock(MAINNET_FORK_BLOCK);
approveAndBridge = new OmnibridgeApproveAndBridge(OMNIBRIDGE);
receiver = makeAddr("E2EOmnibridgeApproveAndBridgeTest: receiver");
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: if this is an alias, feels a bit long :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason it's so long is that I use the format "$indentifier: $short name" unless the function is in the body of a test. This is to avoid that a generic name for an address is reused in both the setup and the body of the test for different purposes (and so add some hidden assumptions).
Note that calling makeAddr("label") twice returns the same address.

shed.trustedExecuteHooks(calls);

// The same hook can still be used if more funds become available
vm.prank(user);
Copy link
Collaborator

Choose a reason for hiding this comment

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

ubernit: we could assert the nonce has not been consumed. I think you do that already by executing the calls

Copy link
Collaborator

Choose a reason for hiding this comment

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

Mmm, actually, you don't deal with the nonce cause you execute the call directly. Ideally we should do a test of pre-mature execution using signed hooks, but you don't need to do that, you've done more than enough! 🙏

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You're absolutely right, I just wanted to have the test ready earlier. 🙂

@fedgiac
Copy link
Collaborator Author

fedgiac commented Jun 13, 2025

Results of testing the AI code reviewer.

I'll split the comments in three groups: (1) good, addressed, where I actually made a change (2) good, unaddressed, where I think it's ok to point them out but for one reason or another I still think it's better to not make a change, and (3) not good. Obviously imho.

Count Good, addressed Good, unaddressed Not good
Main review 7 1 0 6
Nits 15 3 6 6

sebastiantf added a commit to SocketDotTech/approve-and-bridge that referenced this pull request Jul 17, 2025
@anxolin
Copy link
Collaborator

anxolin commented Jan 9, 2026

Closing, although I find this PR useful both as a reference, and also as a more efficient way to bridge using native bridges. Instead of using an intermediate contract (like socket), uses the native bridge.

I would love we could reconsider cleaning it up and merging if one day the FE is willing to also do their part in the UI. But I think is not worth it yet, because we need to also keep the status of the execution in the SDK, which should not be hard, but probably is not something to work now for them

@anxolin anxolin closed this Jan 9, 2026
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.

3 participants