diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f522dcf7..db3b53c11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ * [\#6](https://github.com/crypto-org-chain/relayer/pull/6) Support accurate estimate gas with eth_estimateGas for evm tx. * [\#11](https://github.com/crypto-org-chain/relayer/pull/11) Fix gasFeeCap and gasPrice when new evm tx. * [\#1455](https://github.com/cosmos/relayer/pull/1455) Allow retry for pathEnd to avoid packet message get removed before open channel. +* [\#14](https://github.com/crypto-org-chain/relayer/pull/14) Support batch EVM transactions. ## v0.9.3 diff --git a/relayer/chains/cosmos/precompile/relayer/relayer_caller.abigen.go b/relayer/chains/cosmos/precompile/relayer/relayer_caller.abigen.go new file mode 100644 index 000000000..e1648ab96 --- /dev/null +++ b/relayer/chains/cosmos/precompile/relayer/relayer_caller.abigen.go @@ -0,0 +1,224 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package relayer + +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 +) + +// RelayerCallerMetaData contains all meta data concerning the RelayerCaller contract. +var RelayerCallerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"payloads\",\"type\":\"bytes[]\"}],\"name\":\"batchCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f80fd5b506104338061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c806368be3cf21461002d575b5f80fd5b61004760048036038101906100429190610321565b610049565b005b5f5b81518110156100ee575f606573ffffffffffffffffffffffffffffffffffffffff168383815181106100805761007f610368565b5b602002602001015160405161009591906103e7565b5f604051808303815f865af19150503d805f81146100ce576040519150601f19603f3d011682016040523d82523d5f602084013e6100d3565b606091505b50509050806100e0575f80fd5b50808060010191505061004b565b5050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61014d82610107565b810181811067ffffffffffffffff8211171561016c5761016b610117565b5b80604052505050565b5f61017e6100f2565b905061018a8282610144565b919050565b5f67ffffffffffffffff8211156101a9576101a8610117565b5b602082029050602081019050919050565b5f80fd5b5f80fd5b5f67ffffffffffffffff8211156101dc576101db610117565b5b6101e582610107565b9050602081019050919050565b828183375f83830152505050565b5f61021261020d846101c2565b610175565b90508281526020810184848401111561022e5761022d6101be565b5b6102398482856101f2565b509392505050565b5f82601f83011261025557610254610103565b5b8135610265848260208601610200565b91505092915050565b5f61028061027b8461018f565b610175565b905080838252602082019050602084028301858111156102a3576102a26101ba565b5b835b818110156102ea57803567ffffffffffffffff8111156102c8576102c7610103565b5b8086016102d58982610241565b855260208501945050506020810190506102a5565b5050509392505050565b5f82601f83011261030857610307610103565b5b813561031884826020860161026e565b91505092915050565b5f60208284031215610336576103356100fb565b5b5f82013567ffffffffffffffff811115610353576103526100ff565b5b61035f848285016102f4565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f6103c182610395565b6103cb818561039f565b93506103db8185602086016103a9565b80840191505092915050565b5f6103f282846103b7565b91508190509291505056fea264697066735822122083eb9e4b3048ecd41582b119bf65deec742315dbfd57391afa38ab73a25147c164736f6c63430008190033", +} + +// RelayerCallerABI is the input ABI used to generate the binding from. +// Deprecated: Use RelayerCallerMetaData.ABI instead. +var RelayerCallerABI = RelayerCallerMetaData.ABI + +// RelayerCallerBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use RelayerCallerMetaData.Bin instead. +var RelayerCallerBin = RelayerCallerMetaData.Bin + +// DeployRelayerCaller deploys a new Ethereum contract, binding an instance of RelayerCaller to it. +func DeployRelayerCaller(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *RelayerCaller, error) { + parsed, err := RelayerCallerMetaData.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(RelayerCallerBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &RelayerCaller{RelayerCallerCaller: RelayerCallerCaller{contract: contract}, RelayerCallerTransactor: RelayerCallerTransactor{contract: contract}, RelayerCallerFilterer: RelayerCallerFilterer{contract: contract}}, nil +} + +// RelayerCaller is an auto generated Go binding around an Ethereum contract. +type RelayerCaller struct { + RelayerCallerCaller // Read-only binding to the contract + RelayerCallerTransactor // Write-only binding to the contract + RelayerCallerFilterer // Log filterer for contract events +} + +// RelayerCallerCaller is an auto generated read-only Go binding around an Ethereum contract. +type RelayerCallerCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RelayerCallerTransactor is an auto generated write-only Go binding around an Ethereum contract. +type RelayerCallerTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RelayerCallerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type RelayerCallerFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// RelayerCallerSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type RelayerCallerSession struct { + Contract *RelayerCaller // 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 +} + +// RelayerCallerCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type RelayerCallerCallerSession struct { + Contract *RelayerCallerCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// RelayerCallerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type RelayerCallerTransactorSession struct { + Contract *RelayerCallerTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// RelayerCallerRaw is an auto generated low-level Go binding around an Ethereum contract. +type RelayerCallerRaw struct { + Contract *RelayerCaller // Generic contract binding to access the raw methods on +} + +// RelayerCallerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type RelayerCallerCallerRaw struct { + Contract *RelayerCallerCaller // Generic read-only contract binding to access the raw methods on +} + +// RelayerCallerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type RelayerCallerTransactorRaw struct { + Contract *RelayerCallerTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewRelayerCaller creates a new instance of RelayerCaller, bound to a specific deployed contract. +func NewRelayerCaller(address common.Address, backend bind.ContractBackend) (*RelayerCaller, error) { + contract, err := bindRelayerCaller(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &RelayerCaller{RelayerCallerCaller: RelayerCallerCaller{contract: contract}, RelayerCallerTransactor: RelayerCallerTransactor{contract: contract}, RelayerCallerFilterer: RelayerCallerFilterer{contract: contract}}, nil +} + +// NewRelayerCallerCaller creates a new read-only instance of RelayerCaller, bound to a specific deployed contract. +func NewRelayerCallerCaller(address common.Address, caller bind.ContractCaller) (*RelayerCallerCaller, error) { + contract, err := bindRelayerCaller(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &RelayerCallerCaller{contract: contract}, nil +} + +// NewRelayerCallerTransactor creates a new write-only instance of RelayerCaller, bound to a specific deployed contract. +func NewRelayerCallerTransactor(address common.Address, transactor bind.ContractTransactor) (*RelayerCallerTransactor, error) { + contract, err := bindRelayerCaller(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &RelayerCallerTransactor{contract: contract}, nil +} + +// NewRelayerCallerFilterer creates a new log filterer instance of RelayerCaller, bound to a specific deployed contract. +func NewRelayerCallerFilterer(address common.Address, filterer bind.ContractFilterer) (*RelayerCallerFilterer, error) { + contract, err := bindRelayerCaller(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &RelayerCallerFilterer{contract: contract}, nil +} + +// bindRelayerCaller binds a generic wrapper to an already deployed contract. +func bindRelayerCaller(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := RelayerCallerMetaData.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 (_RelayerCaller *RelayerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RelayerCaller.Contract.RelayerCallerCaller.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 (_RelayerCaller *RelayerCallerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RelayerCaller.Contract.RelayerCallerTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RelayerCaller *RelayerCallerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RelayerCaller.Contract.RelayerCallerTransactor.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 (_RelayerCaller *RelayerCallerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _RelayerCaller.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 (_RelayerCaller *RelayerCallerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _RelayerCaller.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_RelayerCaller *RelayerCallerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _RelayerCaller.Contract.contract.Transact(opts, method, params...) +} + +// BatchCall is a paid mutator transaction binding the contract method 0x68be3cf2. +// +// Solidity: function batchCall(bytes[] payloads) returns() +func (_RelayerCaller *RelayerCallerTransactor) BatchCall(opts *bind.TransactOpts, payloads [][]byte) (*types.Transaction, error) { + return _RelayerCaller.contract.Transact(opts, "batchCall", payloads) +} + +// BatchCall is a paid mutator transaction binding the contract method 0x68be3cf2. +// +// Solidity: function batchCall(bytes[] payloads) returns() +func (_RelayerCaller *RelayerCallerSession) BatchCall(payloads [][]byte) (*types.Transaction, error) { + return _RelayerCaller.Contract.BatchCall(&_RelayerCaller.TransactOpts, payloads) +} + +// BatchCall is a paid mutator transaction binding the contract method 0x68be3cf2. +// +// Solidity: function batchCall(bytes[] payloads) returns() +func (_RelayerCaller *RelayerCallerTransactorSession) BatchCall(payloads [][]byte) (*types.Transaction, error) { + return _RelayerCaller.Contract.BatchCall(&_RelayerCaller.TransactOpts, payloads) +} diff --git a/relayer/chains/cosmos/tx.go b/relayer/chains/cosmos/tx.go index 9b51b8a2f..6182b486f 100644 --- a/relayer/chains/cosmos/tx.go +++ b/relayer/chains/cosmos/tx.go @@ -16,7 +16,6 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "time" sdkerrors "cosmossdk.io/errors" @@ -86,17 +85,21 @@ var ( // Strings for parsing events var ( - spTag = "send_packet" - waTag = "write_acknowledgement" - srcChanTag = "packet_src_channel" - dstChanTag = "packet_dst_channel" - relayerABI abi.ABI + spTag = "send_packet" + waTag = "write_acknowledgement" + srcChanTag = "packet_src_channel" + dstChanTag = "packet_dst_channel" + relayerABI abi.ABI + relayerCallerABI abi.ABI ) func init() { if err := relayerABI.UnmarshalJSON([]byte(relayer.RelayerFunctionsMetaData.ABI)); err != nil { panic(err) } + if err := relayerCallerABI.UnmarshalJSON([]byte(relayer.RelayerCallerMetaData.ABI)); err != nil { + panic(err) + } } // SendMessage attempts to sign, encode & send a RelayerMessage @@ -850,129 +853,6 @@ var messageMap = map[reflect.Type]string{ reflect.TypeOf((*chantypes.MsgTimeoutOnClose)(nil)): "timeoutOnClose", } -type methodCombo struct { - Methods []string - NewMethodName string -} - -var methodCombos = []*methodCombo{ - { - Methods: []string{ - "updateClient", - "connectionOpenInit", - }, - NewMethodName: "updateClientAndConnectionOpenInit", - }, - { - Methods: []string{ - "updateClient", - "connectionOpenTry", - }, - NewMethodName: "updateClientAndConnectionOpenTry", - }, - { - Methods: []string{ - "updateClient", - "connectionOpenAck", - }, - NewMethodName: "updateClientAndConnectionOpenAck", - }, - { - Methods: []string{ - "updateClient", - "connectionOpenConfirm", - }, - NewMethodName: "updateClientAndConnectionOpenConfirm", - }, - { - Methods: []string{ - "updateClient", - "channelOpenInit", - }, - NewMethodName: "updateClientAndChannelOpenInit", - }, - { - Methods: []string{ - "updateClient", - "channelOpenTry", - }, - NewMethodName: "updateClientAndChannelOpenTry", - }, - { - Methods: []string{ - "updateClient", - "channelOpenAck", - }, - NewMethodName: "updateClientAndChannelOpenAck", - }, - { - Methods: []string{ - "updateClient", - "channelOpenConfirm", - }, - NewMethodName: "updateClientAndChannelOpenConfirm", - }, - { - Methods: []string{ - "updateClient", - "channelCloseInit", - }, - NewMethodName: "updateClientAndChannelCloseInit", - }, - { - Methods: []string{ - "updateClient", - "channelCloseConfirm", - }, - NewMethodName: "updateClientAndChannelCloseConfirm", - }, - { - Methods: []string{ - "updateClient", - "recvPacket", - }, - NewMethodName: "updateClientAndRecvPacket", - }, - { - Methods: []string{ - "updateClient", - "acknowledgement", - }, - NewMethodName: "updateClientAndAcknowledgement", - }, - { - Methods: []string{ - "updateClient", - "timeout", - }, - NewMethodName: "updateClientAndTimeout", - }, - { - Methods: []string{ - "updateClient", - "timeoutOnClose", - }, - NewMethodName: "updateClientAndTimeoutOnClose", - }, -} - -func packData(method string, msgs ...sdk.Msg) ([]byte, error) { - args := make([]interface{}, 0, len(msgs)) - for _, m := range msgs { - input, err := proto.Marshal(m) - if err != nil { - return nil, err - } - args = append(args, input) - } - return relayerABI.Pack(method, args...) -} - -type inputAndSigners struct { - Input []byte - Signers []sdk.AccAddress -} - type MsgWithSigners interface { GetSigners() []sdk.AccAddress } @@ -985,52 +865,6 @@ func extractSigners(msg proto.Message) []sdk.AccAddress { return signers } -func extractMsgInputs(msgs []sdk.Msg) (method string, results []*inputAndSigners, err error) { - for _, c := range methodCombos { - if len(msgs) < len(c.Methods) { - continue - } - - matched := true - for i, method := range c.Methods { - if messageMap[reflect.TypeOf(msgs[i])] != method { - matched = false - break - } - } - - if matched { - input, err := packData(c.NewMethodName, msgs[:len(c.Methods)]...) - if err != nil { - return "", nil, err - } - - method = c.NewMethodName - signers := extractSigners(msgs[0]) - results = append(results, &inputAndSigners{input, signers}) - msgs = msgs[len(c.Methods):] - break - } - } - - for _, m := range msgs { - t := reflect.TypeOf(m) - var ok bool - method, ok = messageMap[t] - if !ok { - return "", nil, sdkerrors.Wrapf(legacyerrors.ErrUnknownRequest, "invalid message type %T", m) - } - - input, err := packData(method, m) - if err != nil { - return "", nil, err - } - signers := extractSigners(m) - results = append(results, &inputAndSigners{input, signers}) - } - return -} - func (cc *CosmosProvider) buildEvmMessages( ctx context.Context, txf *tx.Factory, @@ -1056,51 +890,49 @@ func (cc *CosmosProvider) buildEvmMessages( return nil, 0, sdk.Coins{}, errors.New("unsupported extOption") } } - msgs := txb.GetTx().GetMsgs() - txs := make([]sdk.Msg, 0, len(msgs)) fees := make(sdk.Coins, 0) var txGasLimit uint64 = 0 contractAddress := common.HexToAddress(cc.PCfg.PrecompiledContractAddress) blockNumber := new(big.Int) - _, inputs, err := extractMsgInputs(msgs) + msgs := txb.GetTx().GetMsgs() + signers := extractSigners(msgs[0]) + if len(signers) != 1 { + return nil, 0, sdk.Coins{}, sdkerrors.Wrapf(legacyerrors.ErrUnknownRequest, "invalid signers length %d", len(signers)) + } + from, err := convertAddress(signers[0].String()) if err != nil { return nil, 0, sdk.Coins{}, err } - - for i, is := range inputs { - signers := is.Signers - if len(signers) != 1 { - return nil, 0, sdk.Coins{}, sdkerrors.Wrapf(legacyerrors.ErrUnknownRequest, "invalid signers length %d", len(signers)) - } - from, err := convertAddress(signers[0].String()) - if err != nil { - return nil, 0, sdk.Coins{}, err - } - nonce := txf.Sequence() + uint64(i) - amount := big.NewInt(0) - tx := evmtypes.NewTx(chainID, nonce, &contractAddress, amount, gasLimit, gasPrice, gasFeeCap, gasTipCap, is.Input, ðtypes.AccessList{}) - tx.From = from.Bytes() - if err := tx.ValidateBasic(); err != nil { - cc.log.Info("tx failed basic validation", zap.Error(err)) - return nil, 0, sdk.Coins{}, err - } - if err := retry.Do(func() error { - signer := ethtypes.MakeSigner(getChainConfig(chainID), blockNumber) - if err := tx.Sign(signer, cc.Keybase); err != nil { - return err - } - return nil - }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr); err != nil { - return nil, 0, sdk.Coins{}, err - } - if feeAmt := sdkmath.NewIntFromBigInt(tx.GetFee()); feeAmt.Sign() > 0 { - fees = fees.Add(sdk.NewCoin(gasPriceCoin.Denom, feeAmt)) + data, err := cc.getPayloads(contractAddress, msgs) + if err != nil { + return nil, 0, sdk.Coins{}, err + } + nonce := txf.Sequence() + amount := big.NewInt(0) + tx := evmtypes.NewTx( + chainID, nonce, &contractAddress, amount, + gasLimit, gasPrice, gasFeeCap, gasTipCap, + data, ðtypes.AccessList{}) + tx.From = from.Bytes() + if err := tx.ValidateBasic(); err != nil { + cc.log.Info("tx failed basic validation", zap.Error(err)) + return nil, 0, sdk.Coins{}, err + } + if err := retry.Do(func() error { + signer := ethtypes.MakeSigner(getChainConfig(chainID), blockNumber) + if err := tx.Sign(signer, cc.Keybase); err != nil { + return err } - txGasLimit += tx.GetGas() - cc.log.Info("append", zap.String("hash", tx.Hash().String())) - txs = append(txs, tx) + return nil + }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr); err != nil { + return nil, 0, sdk.Coins{}, err + } + if feeAmt := sdkmath.NewIntFromBigInt(tx.GetFee()); feeAmt.Sign() > 0 { + fees = fees.Add(sdk.NewCoin(gasPriceCoin.Denom, feeAmt)) } + txGasLimit += tx.GetGas() + cc.log.Info("append", zap.String("hash", tx.Hash().String())) builder, ok := txb.(authtx.ExtensionOptionsTxBuilder) if !ok { @@ -1112,7 +944,7 @@ func (cc *CosmosProvider) buildEvmMessages( return nil, 0, sdk.Coins{}, err } builder.SetExtensionOptions(option) - builder.SetMsgs(txs...) + builder.SetMsgs(tx) builder.SetFeeAmount(fees) builder.SetGasLimit(txGasLimit) txBytes, err := cc.Cdc.TxConfig.TxEncoder()(builder.GetTx()) @@ -2213,6 +2045,37 @@ func (cc *CosmosProvider) calculateEvmGas(ctx context.Context, arg *evmtypes.Tra return gas, nil } +func (cc *CosmosProvider) getPayloads(to common.Address, msgs []sdk.Msg) ([]byte, error) { + caller := sdk.AccAddress(to.Bytes()).String() + payloads := make([][]byte, 0, len(msgs)) + for _, m := range msgs { + t := reflect.TypeOf(m) + method, ok := messageMap[t] + if !ok { + return nil, fmt.Errorf("invalid type %T", m) + } + elem := reflect.ValueOf(m).Elem() + if elem.Kind() == reflect.Struct { + f := elem.FieldByName("Signer") + if f.IsValid() && f.CanSet() && f.Kind() == reflect.String { + original := f.String() + defer f.SetString(original) + f.SetString(caller) + } + } + input, err := proto.Marshal(m) + if err != nil { + return nil, err + } + payload, err := relayerABI.Pack(method, input) + if err != nil { + return nil, err + } + payloads = append(payloads, payload) + } + return relayerCallerABI.Pack("batchCall", payloads) +} + // CalculateGas simulates a tx to generate the appropriate gas settings before broadcasting a tx. func (cc *CosmosProvider) CalculateGas(ctx context.Context, txf tx.Factory, signingKey string, msgs ...sdk.Msg) (txtypes.SimulateResponse, uint64, error) { if len(cc.PCfg.PrecompiledContractAddress) > 0 && len(cc.PCfg.JSONRPCAddr) > 0 { @@ -2226,58 +2089,38 @@ func (cc *CosmosProvider) CalculateGas(ctx context.Context, txf tx.Factory, sign return txtypes.SimulateResponse{}, 0, err } gasPrice := gasPrices[0].Amount.BigInt() - - var gas uint64 - chErr := make(chan error) - method, inputs, err := extractMsgInputs(msgs) + signers := extractSigners(msgs[0]) + if len(signers) == 0 { + return txtypes.SimulateResponse{}, 0, fmt.Errorf("invalid signers length %d", len(signers)) + } + from, err := convertAddress(signers[0].String()) if err != nil { return txtypes.SimulateResponse{}, 0, err } - - for i, is := range inputs { - go func(i int, is *inputAndSigners) { - signers := is.Signers - if len(signers) == 0 { - chErr <- fmt.Errorf("invalid signers length %d", len(signers)) - return - } - from, err := convertAddress(signers[0].String()) - if err != nil { - chErr <- err - return - } - data := hexutil.Bytes(is.Input) - nonce := hexutil.Uint64(txf.Sequence() + uint64(i)) - arg := &evmtypes.TransactionArgs{ - From: from, - To: &to, - GasPrice: (*hexutil.Big)(gasPrice), - Value: (*hexutil.Big)(big.NewInt(0)), - Nonce: &nonce, - Data: &data, - ChainID: (*hexutil.Big)(chainID), - } - g, err := cc.calculateEvmGas(ctx, arg) - cc.log.Info("calculatedEvmGas", zap.Uint64(method, g)) - if err != nil { - chErr <- err - return - } - g, err = cc.AdjustEstimatedGas(g) - cc.log.Info("adjustedEvmGas", zap.Uint64(method, g)) - if err != nil { - chErr <- err - return - } - - atomic.AddUint64(&gas, g) - chErr <- nil - }(i, is) + bytes, err := cc.getPayloads(to, msgs) + if err != nil { + return txtypes.SimulateResponse{}, 0, err } - for range inputs { - if err = <-chErr; err != nil { - return txtypes.SimulateResponse{}, 0, err - } + data := hexutil.Bytes(bytes) + nonce := hexutil.Uint64(txf.Sequence()) + arg := &evmtypes.TransactionArgs{ + From: from, + To: &to, + GasPrice: (*hexutil.Big)(gasPrice), + Value: (*hexutil.Big)(big.NewInt(0)), + Nonce: &nonce, + Data: &data, + ChainID: (*hexutil.Big)(chainID), + } + gas, err := cc.calculateEvmGas(ctx, arg) + cc.log.Info("calculatedEvmGas", zap.Uint64("batchCall", gas)) + if err != nil { + return txtypes.SimulateResponse{}, 0, err + } + gas, err = cc.AdjustEstimatedGas(gas) + cc.log.Info("adjustedEvmGas", zap.Uint64("batchCall", gas)) + if err != nil { + return txtypes.SimulateResponse{}, 0, err } return txtypes.SimulateResponse{}, gas, err }