Skip to content

Dry-run the entire InboundMessage#1676

Open
yrong wants to merge 31 commits intomainfrom
ron/multi-contract-calls
Open

Dry-run the entire InboundMessage#1676
yrong wants to merge 31 commits intomainfrom
ron/multi-contract-calls

Conversation

@yrong
Copy link
Contributor

@yrong yrong commented Jan 18, 2026

Context

  • Revamp the external v2_dispatch function to allow the UI to dry-run the entire message instead of each individual command.

@codecov
Copy link

codecov bot commented Jan 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.04%. Comparing base (8b31be0) to head (504bb85).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1676      +/-   ##
==========================================
- Coverage   81.20%   81.04%   -0.16%     
==========================================
  Files          22       22              
  Lines        1016      997      -19     
  Branches      196      186      -10     
==========================================
- Hits          825      808      -17     
+ Misses        174      172       -2     
  Partials       17       17              
Flag Coverage Δ
solidity 81.04% <100.00%> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 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.

This comment was marked as off-topic.

yrong and others added 4 commits January 18, 2026 11:45
@yrong yrong changed the title Support multi contract calls Support multiple contract calls Jan 19, 2026

This comment was marked as outdated.

Copilot AI and others added 2 commits January 26, 2026 20:54
* Initial plan

* Fix dispatchSuccess initialization to false

Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>
* Initial plan

* Remove extra blank line for consistency

Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>
@yrong yrong changed the title Support multiple contract calls Enhance atomic execution for v2 calls Jan 26, 2026
Copilot AI and others added 2 commits January 27, 2026 00:31
* Initial plan

* Implement Option A: Propagate InsufficientGasLimit from v2_dispatch

Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>

* Simplify InsufficientGasLimit rethrow logic

Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>

* Fix tests

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>
Co-authored-by: ron <yrong1997@gmail.com>
@Snowfork Snowfork deleted a comment from Copilot AI Jan 27, 2026
@Snowfork Snowfork deleted a comment from Copilot AI Jan 27, 2026
Copilot AI and others added 3 commits January 27, 2026 13:19
…sion (#1690)

* Initial plan

* Fix: Check atomic flag before emitting CommandFailed event

Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yrong <4383920+yrong@users.noreply.github.com>
@yrong yrong marked this pull request as ready for review January 27, 2026 06:27

// independently, such that failures emit a `CommandFailed` event without stopping execution of
// subsequent commands. Returns true if all commands executed successfully, false if any command failed.
function v2_dispatch(InboundMessageV2 calldata message) external onlySelf returns (bool) {
Copy link
Contributor Author

@yrong yrong Jan 27, 2026

Choose a reason for hiding this comment

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

The UI can dry-run this method against the entire InboundMessageV2 message, which is especially useful when we extend use cases to messages composed of multiple calls or commands, such as in the L2 integration.

Copy link
Collaborator

Choose a reason for hiding this comment

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

How can it do though if this function is secured by onlySelf? Can eth_estimateGas simulate a fake origin?

Copy link
Contributor

@alistair-singh alistair-singh Feb 4, 2026

Choose a reason for hiding this comment

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

Correct. Both eth_call and eth_estimateGas can and they do not require signature.

{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [
    {
      "from": "0xYourSenderAddressHere",
      "to": "0xTargetContractAddress",
      "data": "0x..." 
    }
  ],
  "id": 1
}

E.g. from our api:

const tx = await gateway.getFunction("registerToken").populateTransaction(TOKEN_CONTRACT, {
value: ethers.parseEther("1"),
from: ETHEREUM_ACCOUNT_PUBLIC,
})
console.log("Plan tx:", tx)
console.log("Plan gas:", await context.ethereum().estimateGas(tx))
console.log("Plan dry run:", await context.ethereum().call(tx))

@yrong yrong changed the title Enhance atomic execution for v2 calls Dry-run the entire InboundMessage Jan 27, 2026
@Snowfork Snowfork deleted a comment from Copilot AI Jan 27, 2026
Copy link
Contributor

@claravanstaden claravanstaden left a comment

Choose a reason for hiding this comment

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

Probably needs a re-audit. I can ask to audit this together with the L2 contracts?


return allCommandsSucceeded;
// Helper function to dispatch a single command with try-catch for error handling
function v2_dispatchCommand(CommandV2 calldata command, bytes32 origin) external onlySelf {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can perform more refactoring to remove all the intermediate internal calls.

v2_dispatchCommand could call HandlersV2.unlockNativeToken directly, and so on?

} catch (bytes memory reason) {
// If insufficient gas limit, rethrow the error to stop processing
// Otherwise, silently ignore command failures
if (reason.length >= 4 && bytes4(reason) == IGatewayV2.InsufficientGasLimit.selector) {
Copy link
Collaborator

@vgeddes vgeddes Feb 4, 2026

Choose a reason for hiding this comment

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

Not a fan of using Solidity's somewhat awkward exceptions as a control flow mechanism. They are a bit half-baked.

Like in golang, could v2_dispatch return (bool insufficientGas, bool _success)?


// independently, such that failures emit a `CommandFailed` event without stopping execution of
// subsequent commands. Returns true if all commands executed successfully, false if any command failed.
function v2_dispatch(InboundMessageV2 calldata message) external onlySelf returns (bool) {
Copy link
Collaborator

@vgeddes vgeddes Feb 4, 2026

Choose a reason for hiding this comment

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

Its also a bit awkward that InboundMessageV2 is built by BridgeHub, and contains fields such as nonce and origin which are irrelevant for dry-running. It means the UI needs to fake those.

Consider this alternative signature:

function v2_dispatch(Command[] calldata commands) 
returns (
  bool insufficientGasLimit,
  bool failed,
  uint256 failureIndex
)

Then the UI only needs to format commands. The parent for v2_dispatch can interpret the return values to set the final error state.

Copy link
Contributor Author

@yrong yrong Feb 4, 2026

Choose a reason for hiding this comment

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

1fa3437#diff-74470ba8f35b42dbc8805739e92708c0696343e0b1819feb3e7a5ebb77cc623c

@vgeddes I made further refactoring based on your suggestion. However, the failureIndex output, as mentioned above, is not suitable, as multiple commands may fail. Additionally, for the input parameters, the nonce is required when emitting IGatewayV2.CommandFailed(nonce, i), and origin is necessary for calling contracts.

The updated dry-run function signature is as follows:

function v2_dispatch(CommandV2[] calldata commands, bytes32 origin, uint64 nonce)
        external
        onlySelf
        returns (bool insufficientGasLimit, bool success)

Let me know if you'd like to make further adjustments.

@yrong yrong requested a review from vgeddes February 5, 2026 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants