From 703d7b313174530fa5b159993c664adae87cc3dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:39:10 +0000 Subject: [PATCH 1/4] Initial plan From 317bcee24a6d1da42f8794f4b2a928a16e67c65a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:42:40 +0000 Subject: [PATCH 2/4] Implement Option A: Propagate InsufficientGasLimit from v2_dispatch Co-authored-by: yrong <4383920+yrong@users.noreply.github.com> --- contracts/src/Gateway.sol | 8 +++++++- contracts/test/GatewayV2.t.sol | 7 +++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 890b40e3b..37c4d7cf2 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -567,7 +567,13 @@ contract Gateway is IGatewayBase, IGatewayV1, IGatewayV2, IInitializable, IUpgra for (uint256 i = 0; i < message.commands.length; i++) { CommandV2 calldata command = message.commands[i]; try this.v2_dispatchCommand(command, message.origin) {} - catch { + catch (bytes memory reason) { + // Check if the error is InsufficientGasLimit and rethrow it + if (reason.length >= 4 && bytes4(reason) == IGatewayV2.InsufficientGasLimit.selector) { + assembly { + revert(add(reason, 32), mload(reason)) + } + } emit IGatewayV2.CommandFailed(message.nonce, i); if (command.atomic) { revert IGatewayV2.AtomicCommandFailed(); diff --git a/contracts/test/GatewayV2.t.sol b/contracts/test/GatewayV2.t.sol index 31c4715f9..8a3ff0eb4 100644 --- a/contracts/test/GatewayV2.t.sol +++ b/contracts/test/GatewayV2.t.sol @@ -1141,10 +1141,9 @@ contract GatewayV2Test is Test { vm.deal(assetHubAgent, 1 ether); hoax(relayer, 1 ether); - // InsufficientGasLimit during dispatch is caught and emits CommandFailed - // but with very low gas (1), the dispatch might fail differently - vm.expectEmit(true, false, false, true); - emit IGatewayV2.InboundMessageDispatched(1, topic, true, relayerRewardAddress); + // After Option A implementation, InsufficientGasLimit is now propagated + // and causes the entire v2_submit to revert + vm.expectRevert(IGatewayV2.InsufficientGasLimit.selector); IGatewayV2(address(gateway)) .v2_submit( From cb1e2737bdf4b7ad1b71e1e63d3473d36e68f271 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 14:44:03 +0000 Subject: [PATCH 3/4] Simplify InsufficientGasLimit rethrow logic Co-authored-by: yrong <4383920+yrong@users.noreply.github.com> --- contracts/src/Gateway.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 37c4d7cf2..616f146fc 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -570,9 +570,7 @@ contract Gateway is IGatewayBase, IGatewayV1, IGatewayV2, IInitializable, IUpgra catch (bytes memory reason) { // Check if the error is InsufficientGasLimit and rethrow it if (reason.length >= 4 && bytes4(reason) == IGatewayV2.InsufficientGasLimit.selector) { - assembly { - revert(add(reason, 32), mload(reason)) - } + revert IGatewayV2.InsufficientGasLimit(); } emit IGatewayV2.CommandFailed(message.nonce, i); if (command.atomic) { From c20fc6801397edbe74b7945dac7213f36ecf782e Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 27 Jan 2026 00:29:48 +0800 Subject: [PATCH 4/4] Fix tests --- .gitignore | 1 + contracts/test/GatewayV2.t.sol | 54 +++++++++++++++++----------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index be434ea19..70cb2c32c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ lodestar db.sqlite* .pnpm-store /deploy +lib/ diff --git a/contracts/test/GatewayV2.t.sol b/contracts/test/GatewayV2.t.sol index 8a3ff0eb4..8677bb8b1 100644 --- a/contracts/test/GatewayV2.t.sol +++ b/contracts/test/GatewayV2.t.sol @@ -428,15 +428,12 @@ contract GatewayV2Test is Test { // Limit the gas for this test to ensure we hit the NotEnoughGas error uint256 gasLimit = 100_000; vm.deal(relayer, 1 ether); - - vm.expectEmit(true, false, false, true); - emit IGatewayV2.CommandFailed(2, 0); - vm.expectEmit(true, false, false, true); - emit IGatewayV2.InboundMessageDispatched(2, topic, false, relayerRewardAddress); - vm.prank(relayer); - IGatewayV2(address(gateway)) - .v2_submit{gas: gasLimit}(message, proof, makeMockProof(), relayerRewardAddress); + + vm.expectRevert(IGatewayV2.InsufficientGasLimit.selector); + IGatewayV2(address(gateway)).v2_submit{gas: gasLimit}( + message, proof, makeMockProof(), relayerRewardAddress + ); } function mockNativeTokenForSend(address user, uint128 amount) @@ -507,8 +504,9 @@ contract GatewayV2Test is Test { ); hoax(user1); - IGatewayV2(payable(address(gateway))) - .v2_sendMessage{value: 1 ether}("", assets, "", 0.1 ether, 0.4 ether); + IGatewayV2(payable(address(gateway))).v2_sendMessage{value: 1 ether}( + "", assets, "", 0.1 ether, 0.4 ether + ); // Verify asset balances assertEq(assetHubAgent.balance, 1 ether); @@ -533,8 +531,9 @@ contract GatewayV2Test is Test { vm.expectRevert(); hoax(user1); - IGatewayV2(payable(address(gateway))) - .v2_sendMessage{value: 1 ether}("", assets, "", 0.1 ether, 0.4 ether); + IGatewayV2(payable(address(gateway))).v2_sendMessage{value: 1 ether}( + "", assets, "", 0.1 ether, 0.4 ether + ); assertEq(feeToken.balanceOf(assetHubAgent), 0); } @@ -542,16 +541,18 @@ contract GatewayV2Test is Test { function testSendMessageFailsWithInsufficentValue() public { vm.expectRevert(IGatewayV2.InsufficientValue.selector); hoax(user1, 1 ether); - IGatewayV2(payable(address(gateway))) - .v2_sendMessage{value: 0.4 ether}("", new bytes[](0), "", 0.1 ether, 0.4 ether); + IGatewayV2(payable(address(gateway))).v2_sendMessage{value: 0.4 ether}( + "", new bytes[](0), "", 0.1 ether, 0.4 ether + ); } function testSendMessageFailsWithExceededMaximumValue() public { vm.expectRevert(IGatewayV2.ExceededMaximumValue.selector); uint256 value = uint256(type(uint128).max) + 1; hoax(user1, value); - IGatewayV2(payable(address(gateway))) - .v2_sendMessage{value: value}("", new bytes[](0), "", 0.1 ether, 0.4 ether); + IGatewayV2(payable(address(gateway))).v2_sendMessage{value: value}( + "", new bytes[](0), "", 0.1 ether, 0.4 ether + ); } function testUnlockWethSuccess() public { @@ -717,10 +718,9 @@ contract GatewayV2Test is Test { uint256 totalRequired = executionFee + relayerFee; hoax(user1, totalRequired); - IGatewayV2(payable(address(gateway))) - .v2_registerToken{ - value: totalRequired - }(validTokenContract, uint8(0), executionFee, relayerFee); + IGatewayV2(payable(address(gateway))).v2_registerToken{value: totalRequired}( + validTokenContract, uint8(0), executionFee, relayerFee + ); // Verify the token is registered assertTrue(IGatewayV2(address(gateway)).isTokenRegistered(validTokenContract)); @@ -737,10 +737,9 @@ contract GatewayV2Test is Test { vm.expectRevert(IGatewayV2.InsufficientValue.selector); hoax(user1, totalRequired); - IGatewayV2(payable(address(gateway))) - .v2_registerToken{ - value: totalRequired - 1 - }(validTokenContract, uint8(0), executionFee, relayerFee); + IGatewayV2(payable(address(gateway))).v2_registerToken{value: totalRequired - 1}( + validTokenContract, uint8(0), executionFee, relayerFee + ); // Verify token still is not registered after the failed attempt assertFalse(IGatewayV2(address(gateway)).isTokenRegistered(validTokenContract)); @@ -757,8 +756,9 @@ contract GatewayV2Test is Test { vm.expectRevert(IGatewayV2.ExceededMaximumValue.selector); uint256 value = uint256(type(uint128).max) + 1; hoax(user1, value); - IGatewayV2(payable(address(gateway))) - .v2_registerToken{value: value}(validTokenContract, uint8(0), executionFee, relayerFee); + IGatewayV2(payable(address(gateway))).v2_registerToken{value: value}( + validTokenContract, uint8(0), executionFee, relayerFee + ); // Verify token still is not registered after the failed attempt assertFalse(IGatewayV2(address(gateway)).isTokenRegistered(validTokenContract)); @@ -1135,7 +1135,7 @@ contract GatewayV2Test is Test { ); } - function testAgentCallContractRevertedForInsufficientGas() public { + function testAgentCallContractWontRevertForInsufficientGas() public { bytes32 topic = keccak256("topic"); vm.deal(assetHubAgent, 1 ether);