diff --git a/contract/IRefundCallback.go b/contract/IRefundCallback.go new file mode 100644 index 000000000..a1690cff2 --- /dev/null +++ b/contract/IRefundCallback.go @@ -0,0 +1,202 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +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 +) + +// IRefundCallbackMetaData contains all meta data concerning the IRefundCallback contract. +var IRefundCallbackMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"name\":\"refundCallback\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// IRefundCallbackABI is the input ABI used to generate the binding from. +// Deprecated: Use IRefundCallbackMetaData.ABI instead. +var IRefundCallbackABI = IRefundCallbackMetaData.ABI + +// IRefundCallback is an auto generated Go binding around an Ethereum contract. +type IRefundCallback struct { + IRefundCallbackCaller // Read-only binding to the contract + IRefundCallbackTransactor // Write-only binding to the contract + IRefundCallbackFilterer // Log filterer for contract events +} + +// IRefundCallbackCaller is an auto generated read-only Go binding around an Ethereum contract. +type IRefundCallbackCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IRefundCallbackTransactor is an auto generated write-only Go binding around an Ethereum contract. +type IRefundCallbackTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IRefundCallbackFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type IRefundCallbackFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IRefundCallbackSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type IRefundCallbackSession struct { + Contract *IRefundCallback // 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 +} + +// IRefundCallbackCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type IRefundCallbackCallerSession struct { + Contract *IRefundCallbackCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// IRefundCallbackTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type IRefundCallbackTransactorSession struct { + Contract *IRefundCallbackTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IRefundCallbackRaw is an auto generated low-level Go binding around an Ethereum contract. +type IRefundCallbackRaw struct { + Contract *IRefundCallback // Generic contract binding to access the raw methods on +} + +// IRefundCallbackCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type IRefundCallbackCallerRaw struct { + Contract *IRefundCallbackCaller // Generic read-only contract binding to access the raw methods on +} + +// IRefundCallbackTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type IRefundCallbackTransactorRaw struct { + Contract *IRefundCallbackTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewIRefundCallback creates a new instance of IRefundCallback, bound to a specific deployed contract. +func NewIRefundCallback(address common.Address, backend bind.ContractBackend) (*IRefundCallback, error) { + contract, err := bindIRefundCallback(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IRefundCallback{IRefundCallbackCaller: IRefundCallbackCaller{contract: contract}, IRefundCallbackTransactor: IRefundCallbackTransactor{contract: contract}, IRefundCallbackFilterer: IRefundCallbackFilterer{contract: contract}}, nil +} + +// NewIRefundCallbackCaller creates a new read-only instance of IRefundCallback, bound to a specific deployed contract. +func NewIRefundCallbackCaller(address common.Address, caller bind.ContractCaller) (*IRefundCallbackCaller, error) { + contract, err := bindIRefundCallback(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IRefundCallbackCaller{contract: contract}, nil +} + +// NewIRefundCallbackTransactor creates a new write-only instance of IRefundCallback, bound to a specific deployed contract. +func NewIRefundCallbackTransactor(address common.Address, transactor bind.ContractTransactor) (*IRefundCallbackTransactor, error) { + contract, err := bindIRefundCallback(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IRefundCallbackTransactor{contract: contract}, nil +} + +// NewIRefundCallbackFilterer creates a new log filterer instance of IRefundCallback, bound to a specific deployed contract. +func NewIRefundCallbackFilterer(address common.Address, filterer bind.ContractFilterer) (*IRefundCallbackFilterer, error) { + contract, err := bindIRefundCallback(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IRefundCallbackFilterer{contract: contract}, nil +} + +// bindIRefundCallback binds a generic wrapper to an already deployed contract. +func bindIRefundCallback(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IRefundCallbackMetaData.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 (_IRefundCallback *IRefundCallbackRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IRefundCallback.Contract.IRefundCallbackCaller.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 (_IRefundCallback *IRefundCallbackRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IRefundCallback.Contract.IRefundCallbackTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IRefundCallback *IRefundCallbackRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IRefundCallback.Contract.IRefundCallbackTransactor.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 (_IRefundCallback *IRefundCallbackCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IRefundCallback.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 (_IRefundCallback *IRefundCallbackTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IRefundCallback.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IRefundCallback *IRefundCallbackTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IRefundCallback.Contract.contract.Transact(opts, method, params...) +} + +// RefundCallback is a paid mutator transaction binding the contract method 0x3a37fb2e. +// +// Solidity: function refundCallback(uint256 , address[] , uint256[] ) returns() +func (_IRefundCallback *IRefundCallbackTransactor) RefundCallback(opts *bind.TransactOpts, arg0 *big.Int, arg1 []common.Address, arg2 []*big.Int) (*types.Transaction, error) { + return _IRefundCallback.contract.Transact(opts, "refundCallback", arg0, arg1, arg2) +} + +// RefundCallback is a paid mutator transaction binding the contract method 0x3a37fb2e. +// +// Solidity: function refundCallback(uint256 , address[] , uint256[] ) returns() +func (_IRefundCallback *IRefundCallbackSession) RefundCallback(arg0 *big.Int, arg1 []common.Address, arg2 []*big.Int) (*types.Transaction, error) { + return _IRefundCallback.Contract.RefundCallback(&_IRefundCallback.TransactOpts, arg0, arg1, arg2) +} + +// RefundCallback is a paid mutator transaction binding the contract method 0x3a37fb2e. +// +// Solidity: function refundCallback(uint256 , address[] , uint256[] ) returns() +func (_IRefundCallback *IRefundCallbackTransactorSession) RefundCallback(arg0 *big.Int, arg1 []common.Address, arg2 []*big.Int) (*types.Transaction, error) { + return _IRefundCallback.Contract.RefundCallback(&_IRefundCallback.TransactOpts, arg0, arg1, arg2) +} diff --git a/contract/compile.sh b/contract/compile.sh index 7ddbd77db..40da65dd6 100755 --- a/contract/compile.sh +++ b/contract/compile.sh @@ -31,7 +31,7 @@ echo "===> Compiling contracts" [[ ! -d "$project_dir/contract/artifacts" ]] && mkdir -p "$project_dir/contract/artifacts" # add core contracts -contracts=(WFXUpgradable FIP20Upgradable ICrossChain IStaking IFxBridgeLogic) +contracts=(WFXUpgradable FIP20Upgradable ICrossChain IStaking IFxBridgeLogic IRefundCallback) contracts_test=(CrossChainTest StakingTest) # add 3rd party contracts contracts+=(ERC1967Proxy) diff --git a/contract/contract.go b/contract/contract.go index d2552a549..dc5f5f43f 100644 --- a/contract/contract.go +++ b/contract/contract.go @@ -43,6 +43,8 @@ var ( } fxBridgeABI = MustABIJson(IFxBridgeLogicMetaData.ABI) + + bridgeCallRefundCallback = MustABIJson(IRefundCallbackMetaData.ABI) ) type Contract struct { @@ -72,6 +74,10 @@ func GetFxBridgeABI() abi.ABI { return fxBridgeABI } +func GetBridgeCallRefundCallback() abi.ABI { + return bridgeCallRefundCallback +} + func MustDecodeHex(str string) []byte { bz, err := hexutil.Decode(str) if err != nil { diff --git a/solidity/.solhint.json b/solidity/.solhint.json index adaf9aae6..8d0197be5 100644 --- a/solidity/.solhint.json +++ b/solidity/.solhint.json @@ -13,7 +13,6 @@ "endOfLine": "auto" } ], - "reason-string": ["warn", { "maxLength": 64 }], - "one-contract-per-file": "off" + "reason-string": ["warn", { "maxLength": 64 }] } } diff --git a/solidity/contracts/bridge/FxBridgeLogic.sol b/solidity/contracts/bridge/FxBridgeLogic.sol index f1892a654..30f32e7a4 100644 --- a/solidity/contracts/bridge/FxBridgeLogic.sol +++ b/solidity/contracts/bridge/FxBridgeLogic.sol @@ -12,7 +12,8 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import {IERC20ExtensionsUpgradeable} from "./IERC20ExtensionsUpgradeable.sol"; -import {IBridgeCallback, IRefundCallback} from "./ICallback.sol"; +import {IBridgeCallback} from "./IBridgeCallback.sol"; +import {IRefundCallback} from "./IRefundCallback.sol"; /* solhint-disable custom-errors */ diff --git a/solidity/contracts/bridge/FxBridgeLogicETH.sol b/solidity/contracts/bridge/FxBridgeLogicETH.sol index 22a9845bc..998fc22db 100644 --- a/solidity/contracts/bridge/FxBridgeLogicETH.sol +++ b/solidity/contracts/bridge/FxBridgeLogicETH.sol @@ -12,7 +12,8 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import {IERC20ExtensionsUpgradeable} from "./IERC20ExtensionsUpgradeable.sol"; -import {IBridgeCallback, IRefundCallback} from "./ICallback.sol"; +import {IBridgeCallback} from "./IBridgeCallback.sol"; +import {IRefundCallback} from "./IRefundCallback.sol"; /* solhint-disable custom-errors */ diff --git a/solidity/contracts/bridge/ICallback.sol b/solidity/contracts/bridge/IBridgeCallback.sol similarity index 53% rename from solidity/contracts/bridge/ICallback.sol rename to solidity/contracts/bridge/IBridgeCallback.sol index 00441b14e..4944baf16 100644 --- a/solidity/contracts/bridge/ICallback.sol +++ b/solidity/contracts/bridge/IBridgeCallback.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -/* solhint-disable one-contract-per-file */ interface IBridgeCallback { function bridgeCallback( address, @@ -12,12 +11,3 @@ interface IBridgeCallback { bytes memory ) external; } - -/* solhint-disable one-contract-per-file */ -interface IRefundCallback { - function refundCallback( - uint256, - address[] memory, - uint256[] memory - ) external; -} diff --git a/solidity/contracts/bridge/IRefundCallback.sol b/solidity/contracts/bridge/IRefundCallback.sol new file mode 100644 index 000000000..50d134bb1 --- /dev/null +++ b/solidity/contracts/bridge/IRefundCallback.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +interface IRefundCallback { + function refundCallback( + uint256, + address[] memory, + uint256[] memory + ) external; +} diff --git a/x/crosschain/keeper/bridge_call_in.go b/x/crosschain/keeper/bridge_call_in.go index 0eee1b9db..06ebb48d7 100644 --- a/x/crosschain/keeper/bridge_call_in.go +++ b/x/crosschain/keeper/bridge_call_in.go @@ -40,7 +40,7 @@ func (k Keeper) BridgeCallHandler(ctx sdk.Context, msg *types.MsgBridgeCallClaim } // refund event ctx.EventManager().EmitEvent(sdk.NewEvent( - types.EventTypeBridgeCallRefund, + types.EventTypeBridgeCallRefundOut, sdk.NewAttribute(types.AttributeKeyEventNonce, fmt.Sprintf("%d", eventNonce)), sdk.NewAttribute(types.AttributeKeyBridgeCallNonce, fmt.Sprintf("%d", outCall.Nonce)), )) diff --git a/x/crosschain/keeper/bridge_call_in_test.go b/x/crosschain/keeper/bridge_call_in_test.go index 94d0c929d..0f4a81cc5 100644 --- a/x/crosschain/keeper/bridge_call_in_test.go +++ b/x/crosschain/keeper/bridge_call_in_test.go @@ -97,7 +97,7 @@ func (s *KeeperTestSuite) TestBridgeCallHandler() { if t.refund { refundEvent := false for _, event := range s.ctx.EventManager().Events().ToABCIEvents() { - if event.Type == types.EventTypeBridgeCallRefund { + if event.Type == types.EventTypeBridgeCallRefundOut { refundEvent = true } } diff --git a/x/crosschain/keeper/bridge_call_refund.go b/x/crosschain/keeper/bridge_call_refund.go index fc0d91bd5..71f687ead 100644 --- a/x/crosschain/keeper/bridge_call_refund.go +++ b/x/crosschain/keeper/bridge_call_refund.go @@ -1,8 +1,13 @@ package keeper import ( + "math/big" + "strconv" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/functionx/fx-core/v7/contract" "github.com/functionx/fx-core/v7/x/crosschain/types" ) @@ -13,6 +18,23 @@ func (k Keeper) HandleOutgoingBridgeCallRefund(ctx sdk.Context, data *types.Outg panic(err) } + evmErrCause, evmSuccess, isCallback := "", false, false + defer func() { + attrs := []sdk.Attribute{ + sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), + } + if isCallback { + attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyStateSuccess, strconv.FormatBool(evmSuccess))) + if len(evmErrCause) > 0 { + attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyErrCause, evmErrCause)) + } + } + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeBridgeCallRefund, + attrs..., + )) + }() + if k.HasBridgeCallFromMsg(ctx, data.Nonce) { return } @@ -20,6 +42,42 @@ func (k Keeper) HandleOutgoingBridgeCallRefund(ctx sdk.Context, data *types.Outg if err = k.bridgeCallTransferToReceiver(ctx, sender, sender, coins); err != nil { panic(err) } + if data.EventNonce > 0 { + contractAddr := common.BytesToAddress(sender.Bytes()) + account := k.evmKeeper.GetAccount(ctx, contractAddr) + if !account.IsContract() { + return + } + + isCallback = true + maxGasLimit := k.GetParams(ctx).BridgeCallMaxGasLimit + tokens := types.ERC20Tokens(data.Tokens) + args, err := contract.GetBridgeCallRefundCallback().Pack( + "refundCallback", + data.EventNonce, + tokens.GetContracts(), + tokens.GetAmounts(), + ) + if err != nil { + evmErrCause = err.Error() + return + } + txResp, err := k.evmKeeper.CallEVM( + ctx, + k.callbackFrom, + &contractAddr, + big.NewInt(0), + maxGasLimit, + args, + true, + ) + if err != nil { + evmErrCause = err.Error() + } else { + evmSuccess = !txResp.Failed() + evmErrCause = txResp.VmError + } + } } func (k Keeper) DeleteOutgoingBridgeCallRecord(ctx sdk.Context, bridgeCallNonce uint64) { diff --git a/x/crosschain/keeper/keeper.go b/x/crosschain/keeper/keeper.go index 690e7c272..b7065c49d 100644 --- a/x/crosschain/keeper/keeper.go +++ b/x/crosschain/keeper/keeper.go @@ -6,6 +6,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/tendermint/tendermint/libs/log" "github.com/functionx/fx-core/v7/x/crosschain/types" @@ -26,7 +28,8 @@ type Keeper struct { erc20Keeper types.Erc20Keeper evmKeeper types.EVMKeeper - authority string + authority string + callbackFrom common.Address } // NewKeeper returns a new instance of the gravity keeper @@ -53,6 +56,7 @@ func NewKeeper(cdc codec.BinaryCodec, moduleName string, storeKey storetypes.Sto erc20Keeper: erc20Keeper, evmKeeper: evmKeeper, authority: authority, + callbackFrom: common.BytesToAddress(crypto.Keccak256([]byte(types.ModuleName))), } } diff --git a/x/crosschain/mock/expected_keepers_mocks.go b/x/crosschain/mock/expected_keepers_mocks.go index f3b1ca9c1..34257ec1d 100644 --- a/x/crosschain/mock/expected_keepers_mocks.go +++ b/x/crosschain/mock/expected_keepers_mocks.go @@ -22,6 +22,7 @@ import ( types4 "github.com/cosmos/cosmos-sdk/x/staking/types" types5 "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" common "github.com/ethereum/go-ethereum/common" + statedb "github.com/evmos/ethermint/x/evm/statedb" types6 "github.com/evmos/ethermint/x/evm/types" types7 "github.com/functionx/fx-core/v7/types" types8 "github.com/functionx/fx-core/v7/x/crosschain/types" @@ -642,6 +643,20 @@ func (mr *MockEVMKeeperMockRecorder) CallEVM(ctx, from, contract, value, gasLimi return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallEVM", reflect.TypeOf((*MockEVMKeeper)(nil).CallEVM), ctx, from, contract, value, gasLimit, data, commit) } +// GetAccount mocks base method. +func (m *MockEVMKeeper) GetAccount(ctx types.Context, addr common.Address) *statedb.Account { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAccount", ctx, addr) + ret0, _ := ret[0].(*statedb.Account) + return ret0 +} + +// GetAccount indicates an expected call of GetAccount. +func (mr *MockEVMKeeperMockRecorder) GetAccount(ctx, addr any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccount", reflect.TypeOf((*MockEVMKeeper)(nil).GetAccount), ctx, addr) +} + // MockIBCTransferKeeper is a mock of IBCTransferKeeper interface. type MockIBCTransferKeeper struct { ctrl *gomock.Controller diff --git a/x/crosschain/types/events.go b/x/crosschain/types/events.go index b188f7a4d..4d4b88796 100644 --- a/x/crosschain/types/events.go +++ b/x/crosschain/types/events.go @@ -44,6 +44,8 @@ const ( EventTypeProvideLiquidity = "provide_liquidity" AttributeKeyProvideLiquidityTxIds = "provide_liquidity_tx_ids" + EventTypeBridgeCallRefundOut = "bridge_call_refund_out" + EventTypeBridgeCallRefund = "bridge_call_refund" EventTypeBridgeCallResult = "bridge_call_result" diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index 5163d2c18..0083d795f 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -12,6 +12,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" tranfsertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" "github.com/ethereum/go-ethereum/common" + "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" fxtypes "github.com/functionx/fx-core/v7/types" @@ -73,6 +74,7 @@ type Erc20Keeper interface { // EVMKeeper defines the expected EVM keeper interface used on crosschain type EVMKeeper interface { CallEVM(ctx sdk.Context, from common.Address, contract *common.Address, value *big.Int, gasLimit uint64, data []byte, commit bool) (*types.MsgEthereumTxResponse, error) + GetAccount(ctx sdk.Context, addr common.Address) *statedb.Account } type IBCTransferKeeper interface { diff --git a/x/crosschain/types/types.go b/x/crosschain/types/types.go index 0b0de6181..200f512ef 100644 --- a/x/crosschain/types/types.go +++ b/x/crosschain/types/types.go @@ -39,6 +39,24 @@ func (m *ERC20Token) ValidateBasic() error { return nil } +type ERC20Tokens []ERC20Token + +func (e ERC20Tokens) GetContracts() []gethcommon.Address { + contracts := make([]gethcommon.Address, 0, len(e)) + for _, token := range e { + contracts = append(contracts, gethcommon.HexToAddress(token.Contract)) + } + return contracts +} + +func (e ERC20Tokens) GetAmounts() []sdkmath.Int { + amounts := make([]sdkmath.Int, 0, len(e)) + for _, token := range e { + amounts = append(amounts, token.Amount) + } + return amounts +} + // --- BRIDGE VALIDATOR(S) --- // // ValidateBasic performs stateless checks on validity diff --git a/x/evm/precompiles/crosschain/bridge_call.go b/x/evm/precompiles/crosschain/bridge_call.go index e544c77d5..db545b2b5 100644 --- a/x/evm/precompiles/crosschain/bridge_call.go +++ b/x/evm/precompiles/crosschain/bridge_call.go @@ -73,5 +73,5 @@ func (c *Contract) BridgeCall(ctx sdk.Context, evm *vm.EVM, contract *vm.Contrac ); err != nil { return nil, err } - return BridgeCallMethod.Outputs.Pack(true) + return BridgeCallMethod.Outputs.Pack(eventNonce) }