diff --git a/changelog.md b/changelog.md index 8e53bb366b..846a76f2e7 100644 --- a/changelog.md +++ b/changelog.md @@ -15,11 +15,9 @@ ### Refactor * [3118](https://github.com/zeta-chain/node/pull/3118) - zetaclient: remove hsm signer -### Tests - ### Fixes * [3041](https://github.com/zeta-chain/node/pull/3041) - replace libp2p public DHT with private gossip peer discovery and connection gater for inbound connections - +* [3106](https://github.com/zeta-chain/node/pull/3106) - prevent blocked CCTX on out of gas during omnichain calls ## v21.0.0 diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index 22de8a6c51..f4dac2a534 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -55,6 +55,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner e2etests.TestV2ETHDepositAndCallRevertWithCallName, e2etests.TestV2ETHWithdrawAndCallRevertName, e2etests.TestV2ETHWithdrawAndCallRevertWithCallName, + e2etests.TestDepositAndCallOutOfGasName, ), ) diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.abi b/e2e/contracts/testgasconsumer/TestGasConsumer.abi new file mode 100644 index 0000000000..3406cf3e88 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.abi @@ -0,0 +1,47 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestGasConsumer.zContext", + "name": "_context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.bin b/e2e/contracts/testgasconsumer/TestGasConsumer.bin new file mode 100644 index 0000000000..590b637cc1 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.bin @@ -0,0 +1 @@ +6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033 diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.go b/e2e/contracts/testgasconsumer/TestGasConsumer.go new file mode 100644 index 0000000000..b40770c40b --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.go @@ -0,0 +1,231 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package testgasconsumer + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// TestGasConsumerzContext is an auto generated low-level Go binding around an user-defined struct. +type TestGasConsumerzContext struct { + Origin []byte + Sender common.Address + ChainID *big.Int +} + +// TestGasConsumerMetaData contains all meta data concerning the TestGasConsumer contract. +var TestGasConsumerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestGasConsumer.zContext\",\"name\":\"_context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033", +} + +// TestGasConsumerABI is the input ABI used to generate the binding from. +// Deprecated: Use TestGasConsumerMetaData.ABI instead. +var TestGasConsumerABI = TestGasConsumerMetaData.ABI + +// TestGasConsumerBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use TestGasConsumerMetaData.Bin instead. +var TestGasConsumerBin = TestGasConsumerMetaData.Bin + +// DeployTestGasConsumer deploys a new Ethereum contract, binding an instance of TestGasConsumer to it. +func DeployTestGasConsumer(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TestGasConsumer, error) { + parsed, err := TestGasConsumerMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestGasConsumerBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &TestGasConsumer{TestGasConsumerCaller: TestGasConsumerCaller{contract: contract}, TestGasConsumerTransactor: TestGasConsumerTransactor{contract: contract}, TestGasConsumerFilterer: TestGasConsumerFilterer{contract: contract}}, nil +} + +// TestGasConsumer is an auto generated Go binding around an Ethereum contract. +type TestGasConsumer struct { + TestGasConsumerCaller // Read-only binding to the contract + TestGasConsumerTransactor // Write-only binding to the contract + TestGasConsumerFilterer // Log filterer for contract events +} + +// TestGasConsumerCaller is an auto generated read-only Go binding around an Ethereum contract. +type TestGasConsumerCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TestGasConsumerTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TestGasConsumerFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestGasConsumerSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TestGasConsumerSession struct { + Contract *TestGasConsumer // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestGasConsumerCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TestGasConsumerCallerSession struct { + Contract *TestGasConsumerCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TestGasConsumerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TestGasConsumerTransactorSession struct { + Contract *TestGasConsumerTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestGasConsumerRaw is an auto generated low-level Go binding around an Ethereum contract. +type TestGasConsumerRaw struct { + Contract *TestGasConsumer // Generic contract binding to access the raw methods on +} + +// TestGasConsumerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TestGasConsumerCallerRaw struct { + Contract *TestGasConsumerCaller // Generic read-only contract binding to access the raw methods on +} + +// TestGasConsumerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TestGasConsumerTransactorRaw struct { + Contract *TestGasConsumerTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTestGasConsumer creates a new instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumer(address common.Address, backend bind.ContractBackend) (*TestGasConsumer, error) { + contract, err := bindTestGasConsumer(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &TestGasConsumer{TestGasConsumerCaller: TestGasConsumerCaller{contract: contract}, TestGasConsumerTransactor: TestGasConsumerTransactor{contract: contract}, TestGasConsumerFilterer: TestGasConsumerFilterer{contract: contract}}, nil +} + +// NewTestGasConsumerCaller creates a new read-only instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerCaller(address common.Address, caller bind.ContractCaller) (*TestGasConsumerCaller, error) { + contract, err := bindTestGasConsumer(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TestGasConsumerCaller{contract: contract}, nil +} + +// NewTestGasConsumerTransactor creates a new write-only instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerTransactor(address common.Address, transactor bind.ContractTransactor) (*TestGasConsumerTransactor, error) { + contract, err := bindTestGasConsumer(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TestGasConsumerTransactor{contract: contract}, nil +} + +// NewTestGasConsumerFilterer creates a new log filterer instance of TestGasConsumer, bound to a specific deployed contract. +func NewTestGasConsumerFilterer(address common.Address, filterer bind.ContractFilterer) (*TestGasConsumerFilterer, error) { + contract, err := bindTestGasConsumer(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TestGasConsumerFilterer{contract: contract}, nil +} + +// bindTestGasConsumer binds a generic wrapper to an already deployed contract. +func bindTestGasConsumer(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TestGasConsumerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestGasConsumer *TestGasConsumerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestGasConsumer.Contract.TestGasConsumerCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestGasConsumer *TestGasConsumerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestGasConsumer.Contract.TestGasConsumerTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestGasConsumer *TestGasConsumerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestGasConsumer.Contract.TestGasConsumerTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestGasConsumer *TestGasConsumerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestGasConsumer.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestGasConsumer *TestGasConsumerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestGasConsumer.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestGasConsumer *TestGasConsumerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestGasConsumer.Contract.contract.Transact(opts, method, params...) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerTransactor) OnCall(opts *bind.TransactOpts, _context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.contract.Transact(opts, "onCall", _context, _zrc20, _amount, _message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerSession) OnCall(_context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.Contract.OnCall(&_TestGasConsumer.TransactOpts, _context, _zrc20, _amount, _message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) _context, address _zrc20, uint256 _amount, bytes _message) returns() +func (_TestGasConsumer *TestGasConsumerTransactorSession) OnCall(_context TestGasConsumerzContext, _zrc20 common.Address, _amount *big.Int, _message []byte) (*types.Transaction, error) { + return _TestGasConsumer.Contract.OnCall(&_TestGasConsumer.TransactOpts, _context, _zrc20, _amount, _message) +} diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.json b/e2e/contracts/testgasconsumer/TestGasConsumer.json new file mode 100644 index 0000000000..9f80d60d5f --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.json @@ -0,0 +1,50 @@ +{ + "abi": [ + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestGasConsumer.zContext", + "name": "_context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bin": "6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033" +} diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.sol b/e2e/contracts/testgasconsumer/TestGasConsumer.sol new file mode 100644 index 0000000000..4d1d322cd2 --- /dev/null +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +// TestGasConsumer is a contract used to simulate high gas consumption +contract TestGasConsumer { + // used to simulate gas consumption + uint256[] private storageArray; + + struct zContext { + bytes origin; + address sender; + uint256 chainID; + } + + // Universal contract interface on ZEVM + function onCall( + zContext calldata _context, + address _zrc20, + uint256 _amount, + bytes calldata _message + ) + external + { + consumeGas(); + } + + function consumeGas() internal { + // Approximate target gas consumption + uint256 targetGas = 5000000; + // Approximate gas cost for a single storage write + uint256 storageWriteGasCost = 20000; + uint256 iterations = targetGas / storageWriteGasCost; + + // Perform the storage writes + for (uint256 i = 0; i < iterations; i++) { + storageArray.push(i); + } + + // Reset the storage array to avoid accumulation of storage cost + delete storageArray; + } +} \ No newline at end of file diff --git a/e2e/contracts/testgasconsumer/bindings.go b/e2e/contracts/testgasconsumer/bindings.go new file mode 100644 index 0000000000..baa99020ba --- /dev/null +++ b/e2e/contracts/testgasconsumer/bindings.go @@ -0,0 +1,8 @@ +//go:generate sh -c "solc TestGasConsumer.sol --evm-version london --combined-json abi,bin | jq '.contracts.\"TestGasConsumer.sol:TestGasConsumer\"' > TestGasConsumer.json" +//go:generate sh -c "cat TestGasConsumer.json | jq .abi > TestGasConsumer.abi" +//go:generate sh -c "cat TestGasConsumer.json | jq .bin | tr -d '\"' > TestGasConsumer.bin" +//go:generate sh -c "abigen --abi TestGasConsumer.abi --bin TestGasConsumer.bin --pkg testgasconsumer --type TestGasConsumer --out TestGasConsumer.go" + +package testgasconsumer + +var _ TestGasConsumer diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 82764cb537..c1c5d8b533 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -149,6 +149,7 @@ const ( TestV2ETHWithdrawAndCallThroughContractName = "v2_eth_withdraw_and_call_through_contract" TestV2ETHWithdrawAndCallRevertName = "v2_eth_withdraw_and_call_revert" TestV2ETHWithdrawAndCallRevertWithCallName = "v2_eth_withdraw_and_call_revert_with_call" + TestDepositAndCallOutOfGasName = "deposit_and_call_out_of_gas" TestV2ERC20DepositName = "v2_erc20_deposit" TestV2ERC20DepositAndCallName = "v2_erc20_deposit_and_call" TestV2ERC20DepositAndCallNoMessageName = "v2_erc20_deposit_and_call_no_message" @@ -896,6 +897,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestV2ETHWithdrawAndCallRevertWithCall, ), + runner.NewE2ETest( + TestDepositAndCallOutOfGasName, + "deposit Ether into ZEVM and call a contract that runs out of gas", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestDepositAndCallOutOfGas, + ), runner.NewE2ETest( TestV2ERC20DepositName, "deposit ERC20 into ZEVM using V2 contract", diff --git a/e2e/e2etests/test_deposit_and_call_out_of_gas.go b/e2e/e2etests/test_deposit_and_call_out_of_gas.go new file mode 100644 index 0000000000..4b42995979 --- /dev/null +++ b/e2e/e2etests/test_deposit_and_call_out_of_gas.go @@ -0,0 +1,37 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/node/e2e/contracts/testgasconsumer" + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestDepositAndCallOutOfGas tests that a deposit and call that consumer all gas will revert +func TestDepositAndCallOutOfGas(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := parseBigInt(r, args[0]) + + // Deploy the GasConsumer contract + gasConsumerAddress, _, _, err := testgasconsumer.DeployTestGasConsumer(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + + // perform the deposit and call to the GasConsumer contract + tx := r.V2ETHDepositAndCall( + gasConsumerAddress, + amount, + []byte(randomPayload(r)), + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be reverted + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_call") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) +} diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go index b2b76dc428..c92116ca4b 100644 --- a/x/fungible/keeper/v2_evm.go +++ b/x/fungible/keeper/v2_evm.go @@ -15,6 +15,9 @@ import ( "github.com/zeta-chain/node/x/fungible/types" ) +// gatewayGasLimit is the gas limit for the gateway functions +var gatewayGasLimit = big.NewInt(1_000_000) + // CallUpdateGatewayAddress calls the updateGatewayAddress function on the ZRC20 contract // function updateGatewayAddress(address addr) func (k Keeper) CallUpdateGatewayAddress( @@ -33,7 +36,7 @@ func (k Keeper) CallUpdateGatewayAddress( types.ModuleAddressEVM, zrc20Address, BigIntZero, - nil, + gatewayGasLimit, true, false, "updateGatewayAddress", @@ -83,7 +86,7 @@ func (k Keeper) CallDepositAndCallZRC20( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "depositAndCall0", @@ -133,7 +136,7 @@ func (k Keeper) CallExecute( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "execute", @@ -179,7 +182,7 @@ func (k Keeper) CallExecuteRevert( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "executeRevert", @@ -231,7 +234,7 @@ func (k Keeper) CallDepositAndRevert( types.ModuleAddressEVM, gatewayAddr, BigIntZero, - nil, + gatewayGasLimit, true, false, "depositAndRevert",