diff --git a/bridges/ethMultiversX/bridgeExecutor.go b/bridges/ethMultiversX/bridgeExecutor.go index 2570dda8..7c9141af 100644 --- a/bridges/ethMultiversX/bridgeExecutor.go +++ b/bridges/ethMultiversX/bridgeExecutor.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-chain-core-go/core/check" logger "github.com/multiversx/mx-chain-logger-go" @@ -447,11 +448,58 @@ func (executor *bridgeExecutor) GetAndStoreBatchFromEthereum(ctx context.Context ErrBatchNotFound, nonce, batch.ID, len(batch.Deposits)) } + batch, err = executor.addBatchSCMetadata(ctx, batch) + if err != nil { + return err + } executor.batch = batch return nil } +// addBatchSCMetadata fetches the logs containing sc calls metadata for the current batch +func (executor *bridgeExecutor) addBatchSCMetadata(ctx context.Context, transfers *clients.TransferBatch) (*clients.TransferBatch, error) { + if transfers == nil { + return nil, ErrNilBatch + } + + if !executor.hasSCCalls(transfers) { + return transfers, nil + } + + events, err := executor.ethereumClient.GetBatchSCMetadata(ctx, transfers.ID) + if err != nil { + return nil, err + } + + for i, t := range transfers.Deposits { + transfers.Deposits[i] = executor.addMetadataToTransfer(t, events) + } + + return transfers, nil +} + +func (executor *bridgeExecutor) addMetadataToTransfer(transfer *clients.DepositTransfer, events []*contract.SCExecProxyERC20SCDeposit) *clients.DepositTransfer { + for _, event := range events { + if event.DepositNonce == transfer.Nonce { + transfer.ExtraGasLimit = event.MvxGasLimit + transfer.Data = []byte(event.CallData) + return transfer + } + } + return transfer +} + +func (executor *bridgeExecutor) hasSCCalls(transfers *clients.TransferBatch) bool { + for _, t := range transfers.Deposits { + if executor.ethereumClient.IsDepositSCCall(t) { + return true + } + } + + return false +} + // WasTransferPerformedOnEthereum returns true if the batch was performed on Ethereum func (executor *bridgeExecutor) WasTransferPerformedOnEthereum(ctx context.Context) (bool, error) { if executor.batch == nil { diff --git a/bridges/ethMultiversX/bridgeExecutor_test.go b/bridges/ethMultiversX/bridgeExecutor_test.go index 3cfc5421..e8b53d1b 100644 --- a/bridges/ethMultiversX/bridgeExecutor_test.go +++ b/bridges/ethMultiversX/bridgeExecutor_test.go @@ -1,6 +1,7 @@ package ethmultiversx import ( + "bytes" "context" "errors" "fmt" @@ -11,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/testsCommon" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" @@ -362,6 +364,42 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing assert.True(t, expectedBatch == executor.batch) }) + + t.Run("should add deposits metadata for sc calls", func(t *testing.T) { + args := createMockExecutorArgs() + providedNonce := uint64(8346) + depositNonce := uint64(100) + depositData := "testData" + expectedBatch := &clients.TransferBatch{ + ID: providedNonce, + Deposits: []*clients.DepositTransfer{ + { + Nonce: depositNonce, + }, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64) ([]*contract.SCExecProxyERC20SCDeposit, error) { + return []*contract.SCExecProxyERC20SCDeposit{{ + DepositNonce: depositNonce, + CallData: depositData, + }}, nil + }, + IsDepositSCCallCalled: func(deposit *clients.DepositTransfer) bool { + return deposit.Nonce == depositNonce + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.Nil(t, err) + assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing + assert.True(t, bytes.Equal([]byte(depositData), executor.batch.Deposits[0].Data)) + }) } func TestEthToMultiversXBridgeExecutor_GetLastExecutedEthBatchIDFromMultiversX(t *testing.T) { diff --git a/bridges/ethMultiversX/interface.go b/bridges/ethMultiversX/interface.go index 63a6f706..22dd415a 100644 --- a/bridges/ethMultiversX/interface.go +++ b/bridges/ethMultiversX/interface.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" ) // MultiversXClient defines the behavior of the MultiversX client able to communicate with the MultiversX chain @@ -38,12 +39,14 @@ type EthereumClient interface { GetBatch(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) WasExecuted(ctx context.Context, batchID uint64) (bool, error) GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, error) + IsDepositSCCall(deposit *clients.DepositTransfer) bool BroadcastSignatureForMessageHash(msgHash common.Hash) ExecuteTransfer(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) GetTransactionsStatuses(ctx context.Context, batchId uint64) ([]byte, error) GetQuorumSize(ctx context.Context) (*big.Int, error) IsQuorumReached(ctx context.Context, msgHash common.Hash) (bool, error) + GetBatchSCMetadata(ctx context.Context, nonce uint64) ([]*contract.SCExecProxyERC20SCDeposit, error) CheckClientAvailability(ctx context.Context) error IsInterfaceNil() bool } diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/constants.go b/bridges/ethMultiversX/steps/ethToMultiversX/constants.go index 4aec9996..32cf2aa5 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/constants.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/constants.go @@ -4,7 +4,7 @@ const ( // GettingPendingBatchFromEthereum is the step identifier for fetching the pending batch from the Ethereum chain GettingPendingBatchFromEthereum = "get pending batch from Ethereum" - // ProposingTransferOnMultiversX is the step idetifier for proposing transfer on MultiversX + // ProposingTransferOnMultiversX is the step identifier for proposing transfer on MultiversX ProposingTransferOnMultiversX = "propose transfer" // SigningProposedTransferOnMultiversX is the step identifier for signing proposed transfer diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go index 3ba437a1..f13917b2 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go @@ -13,9 +13,11 @@ import ( var expectedError = errors.New("expected error") var testBatch = &clients.TransferBatch{ - ID: 112233, - Deposits: nil, - Statuses: nil, + ID: 112233, + Deposits: []*clients.DepositTransfer{ + {}, + }, + Statuses: []byte{0}, } func TestExecuteGetPending(t *testing.T) { diff --git a/bridges/ethMultiversX/steps/interface.go b/bridges/ethMultiversX/steps/interface.go index d842b6c3..629bf2f6 100644 --- a/bridges/ethMultiversX/steps/interface.go +++ b/bridges/ethMultiversX/steps/interface.go @@ -15,6 +15,7 @@ type Executor interface { GetBatchFromMultiversX(ctx context.Context) (*clients.TransferBatch, error) StoreBatchFromMultiversX(batch *clients.TransferBatch) error GetStoredBatch() *clients.TransferBatch + GetLastExecutedEthBatchIDFromMultiversX(ctx context.Context) (uint64, error) VerifyLastDepositNonceExecutedOnEthereumBatch(ctx context.Context) error diff --git a/clients/batch.go b/clients/batch.go index 601c0630..6e01abeb 100644 --- a/clients/batch.go +++ b/clients/batch.go @@ -74,12 +74,15 @@ type DepositTransfer struct { ConvertedTokenBytes []byte `json:"-"` DisplayableToken string `json:"token"` Amount *big.Int `json:"amount"` + ExtraGasLimit uint64 `json:"gasLimit"` + Data []byte `json:"-"` + DisplayableData string `json:"data"` } // String will convert the deposit transfer to a string func (dt *DepositTransfer) String() string { - return fmt.Sprintf("to: %s, from: %s, token address: %s, amount: %v, deposit nonce: %d", - dt.DisplayableTo, dt.DisplayableFrom, dt.DisplayableToken, dt.Amount, dt.Nonce) + return fmt.Sprintf("to: %s, from: %s, token address: %s, amount: %v, deposit nonce: %d, gas limit: %d, data: %s", + dt.DisplayableTo, dt.DisplayableFrom, dt.DisplayableToken, dt.Amount, dt.Nonce, dt.ExtraGasLimit, dt.DisplayableData) } // Clone will deep clone the current DepositTransfer instance @@ -94,12 +97,15 @@ func (dt *DepositTransfer) Clone() *DepositTransfer { ConvertedTokenBytes: make([]byte, len(dt.ConvertedTokenBytes)), DisplayableToken: dt.DisplayableToken, Amount: big.NewInt(0), + Data: make([]byte, len(dt.Data)), + DisplayableData: dt.DisplayableData, } copy(cloned.ToBytes, dt.ToBytes) copy(cloned.FromBytes, dt.FromBytes) copy(cloned.TokenBytes, dt.TokenBytes) copy(cloned.ConvertedTokenBytes, dt.ConvertedTokenBytes) + copy(cloned.Data, dt.Data) if dt.Amount != nil { cloned.Amount.Set(dt.Amount) } diff --git a/clients/batchValidator/batchValidator_test.go b/clients/batchValidator/batchValidator_test.go index 5d4aaad2..2b3a7e60 100644 --- a/clients/batchValidator/batchValidator_test.go +++ b/clients/batchValidator/batchValidator_test.go @@ -92,7 +92,7 @@ func TestBatchValidator_ValidateBatch(t *testing.T) { }, Statuses: []byte{0x3, 0x4}, } - expectedJsonString := `{"batchId":1,"deposits":[{"nonce":1,"to":"to1","from":"from1","token":"token1","amount":1000000000000000000001},{"nonce":2,"to":"to2","from":"from2","token":"token2","amount":1000000000000000000002}],"statuses":"AwQ="}` + expectedJsonString := `{"batchId":1,"deposits":[{"nonce":1,"to":"to1","from":"from1","token":"token1","amount":1000000000000000000001,"gasLimit":0,"data":""},{"nonce":2,"to":"to2","from":"from2","token":"token2","amount":1000000000000000000002,"gasLimit":0,"data":""}],"statuses":"AwQ="}` t.Run("server errors with Bad Request, but no reason", func(t *testing.T) { t.Parallel() diff --git a/clients/batch_test.go b/clients/batch_test.go index 2f362c4d..0cf44349 100644 --- a/clients/batch_test.go +++ b/clients/batch_test.go @@ -20,6 +20,7 @@ func TestDepositTransfer_Clone(t *testing.T) { DisplayableToken: "token", Amount: big.NewInt(7463), ConvertedTokenBytes: []byte("converted token"), + Data: []byte("tx data"), } cloned := dt.Clone() @@ -42,7 +43,7 @@ func TestDepositTransfer_String(t *testing.T) { Amount: big.NewInt(7463), } - expectedString := "to: to, from: from, token address: token, amount: 7463, deposit nonce: 112334" + expectedString := "to: to, from: from, token address: token, amount: 7463, deposit nonce: 112334, gas limit: 0, data: " assert.Equal(t, expectedString, dt.String()) } @@ -62,6 +63,7 @@ func TestTransferBatch_Clone(t *testing.T) { DisplayableToken: "token1", Amount: big.NewInt(3344), ConvertedTokenBytes: []byte("converted token1"), + Data: []byte("tx data"), }, { Nonce: 2, @@ -73,6 +75,7 @@ func TestTransferBatch_Clone(t *testing.T) { DisplayableToken: "token2", Amount: big.NewInt(5566), ConvertedTokenBytes: []byte("converted token2"), + Data: []byte("tx data"), }, }, Statuses: []byte{Executed, Rejected}, @@ -115,8 +118,8 @@ func TestTransferBatch_String(t *testing.T) { } expectedString := `Batch id 2243: - to: to1, from: from1, token address: token1, amount: 3344, deposit nonce: 1 - to: to2, from: from2, token address: token2, amount: 5566, deposit nonce: 2 + to: to1, from: from1, token address: token1, amount: 3344, deposit nonce: 1, gas limit: 0, data: + to: to2, from: from2, token address: token2, amount: 5566, deposit nonce: 2, gas limit: 0, data: Statuses: 0304` assert.Equal(t, expectedString, tb.String()) } diff --git a/clients/ethereum/client.go b/clients/ethereum/client.go index 9062c3f3..133c5ae6 100644 --- a/clients/ethereum/client.go +++ b/clients/ethereum/client.go @@ -1,18 +1,21 @@ package ethereum import ( + "bytes" "context" "crypto/ecdsa" "fmt" "math/big" "sync" + "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/crypto" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/core" chainCore "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -42,6 +45,7 @@ type ArgsEthereumClient struct { TokensMapper TokensMapper SignatureHolder SignaturesHolder SafeContractAddress common.Address + SCExecProxyAddress common.Address GasHandler GasHandler TransferGasLimitBase uint64 TransferGasLimitForEach uint64 @@ -59,6 +63,7 @@ type client struct { tokensMapper TokensMapper signatureHolder SignaturesHolder safeContractAddress common.Address + scExecProxyAddress common.Address gasHandler GasHandler transferGasLimitBase uint64 transferGasLimitForEach uint64 @@ -93,6 +98,7 @@ func NewEthereumClient(args ArgsEthereumClient) (*client, error) { tokensMapper: args.TokensMapper, signatureHolder: args.SignatureHolder, safeContractAddress: args.SafeContractAddress, + scExecProxyAddress: args.SCExecProxyAddress, gasHandler: args.GasHandler, transferGasLimitBase: args.TransferGasLimitBase, transferGasLimitForEach: args.TransferGasLimitForEach, @@ -204,6 +210,42 @@ func (c *client) GetBatch(ctx context.Context, nonce uint64) (*clients.TransferB return transferBatch, nil } +// GetBatchSCMetadata returns the emitted logs in a batch that hold metadata for SC execution on MVX +func (c *client) GetBatchSCMetadata(ctx context.Context, nonce uint64) ([]*contract.SCExecProxyERC20SCDeposit, error) { + scExecAbi, err := contract.SCExecProxyMetaData.GetAbi() + if err != nil { + return nil, err + } + + query := ethereum.FilterQuery{ + Addresses: []common.Address{c.scExecProxyAddress}, + Topics: [][]common.Hash{ + {scExecAbi.Events["ERC20SCDeposit"].ID}, + {common.BytesToHash(new(big.Int).SetUint64(nonce).Bytes())}, + }, + } + + logs, err := c.clientWrapper.FilterLogs(ctx, query) + if err != nil { + return nil, err + } + + depositEvents := make([]*contract.SCExecProxyERC20SCDeposit, 0) + for _, vLog := range logs { + event := new(contract.SCExecProxyERC20SCDeposit) + err = scExecAbi.UnpackIntoInterface(event, "ERC20SCDeposit", vLog.Data) + if err != nil { + return nil, err + } + + // Add this manually since UnpackIntoInterface only unpacks non-indexed arguments + event.BatchNonce = nonce + depositEvents = append(depositEvents, event) + } + + return depositEvents, nil +} + // WasExecuted returns true if the batch ID was executed func (c *client) WasExecuted(ctx context.Context, batchID uint64) (bool, error) { return c.clientWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(batchID)) @@ -245,6 +287,15 @@ func (c *client) GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, return crypto.Keccak256Hash(append([]byte(messagePrefix), hash.Bytes()...)), nil } +// IsDepositSCCall checks whether a deposit should be treated as a SC interaction +func (c *client) IsDepositSCCall(deposit *clients.DepositTransfer) bool { + if deposit == nil { + return false + } + + return bytes.Equal(deposit.FromBytes, c.scExecProxyAddress.Bytes()) +} + func generateTransferArgs() (abi.Arguments, error) { addressesType, err := abi.NewType("address[]", "", nil) if err != nil { diff --git a/clients/ethereum/client_test.go b/clients/ethereum/client_test.go index 0e7aa2e4..351ec5f7 100644 --- a/clients/ethereum/client_test.go +++ b/clients/ethereum/client_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -52,6 +53,7 @@ func createMockEthereumClientArgs() ArgsEthereumClient { }, SignatureHolder: &testsCommon.SignaturesHolderStub{}, SafeContractAddress: testsCommon.CreateRandomEthereumAddress(), + SCExecProxyAddress: testsCommon.CreateRandomEthereumAddress(), GasHandler: &testsCommon.GasHandlerStub{}, TransferGasLimitBase: 50, TransferGasLimitForEach: 20, @@ -813,6 +815,43 @@ func TestClient_IsQuorumReached(t *testing.T) { }) } +func TestClient_IsDepositSCCall(t *testing.T) { + t.Parallel() + + t.Run("nil deposit returns false", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + c, _ := NewEthereumClient(args) + + assert.False(t, c.IsDepositSCCall(nil)) + }) + + t.Run("returns true for matching addresses", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + c, _ := NewEthereumClient(args) + + deposit := &clients.DepositTransfer{ + FromBytes: args.SCExecProxyAddress.Bytes(), + } + assert.True(t, c.IsDepositSCCall(deposit)) + }) + + t.Run("returns false for non matching addresses", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + c, _ := NewEthereumClient(args) + + deposit := &clients.DepositTransfer{ + FromBytes: []byte("different_addr"), + } + assert.False(t, c.IsDepositSCCall(deposit)) + }) +} + func TestClient_CheckClientAvailability(t *testing.T) { t.Parallel() @@ -903,6 +942,60 @@ func TestClient_CheckClientAvailability(t *testing.T) { }) } +func TestClient_GetBatchSCMetadata(t *testing.T) { + t.Parallel() + + t.Run("returns error on filter logs error", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("filter logs err") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return nil, expectedErr + }, + } + c, _ := NewEthereumClient(args) + batch, err := c.GetBatchSCMetadata(context.Background(), 0) + assert.Nil(t, batch) + assert.Equal(t, expectedErr, err) + }) + + t.Run("returns expected logs", func(t *testing.T) { + scExecAbi, _ := contract.SCExecProxyMetaData.GetAbi() + expectedEvent := &contract.SCExecProxyERC20SCDeposit{ + BatchNonce: 1, + DepositNonce: 1, + MvxGasLimit: 1, + CallData: "call_data_to_unpack", + } + + eventInputs := scExecAbi.Events["ERC20SCDeposit"].Inputs.NonIndexed() + packedArgs, _ := eventInputs.Pack(expectedEvent.DepositNonce, + expectedEvent.MvxGasLimit, expectedEvent.CallData) + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return []types.Log{ + { + Data: packedArgs, + }, + }, nil + }, + } + c, _ := NewEthereumClient(args) + batch, err := c.GetBatchSCMetadata(context.Background(), expectedEvent.BatchNonce) + + assert.Nil(t, err) + assert.Equal(t, 1, len(batch)) + assert.Equal(t, expectedEvent.BatchNonce, batch[0].BatchNonce) + assert.Equal(t, expectedEvent.DepositNonce, batch[0].DepositNonce) + assert.Equal(t, expectedEvent.MvxGasLimit, batch[0].MvxGasLimit) + assert.Equal(t, expectedEvent.CallData, batch[0].CallData) + }) +} + func resetClient(c *client) { c.mut.Lock() c.retriesAvailabilityCheck = 0 diff --git a/clients/ethereum/contract/scExecProxy.go b/clients/ethereum/contract/scExecProxy.go new file mode 100644 index 00000000..02ccf932 --- /dev/null +++ b/clients/ethereum/contract/scExecProxy.go @@ -0,0 +1,658 @@ +// 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 +) + +// SCExecProxyMetaData contains all meta data concerning the SCExecProxy contract. +var SCExecProxyMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"contractERC20Safe\",\"name\":\"erc20Safe\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"batchNonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"depositNonce\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"mvxGasLimit\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"callData\",\"type\":\"string\"}],\"name\":\"ERC20SCDeposit\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientAddress\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"mvxGasLimit\",\"type\":\"uint64\"},{\"internalType\":\"string\",\"name\":\"callData\",\"type\":\"string\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isSafePaused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"safe\",\"outputs\":[{\"internalType\":\"contractERC20Safe\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractERC20Safe\",\"name\":\"erc20Safe\",\"type\":\"address\"}],\"name\":\"setSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// SCExecProxyABI is the input ABI used to generate the binding from. +// Deprecated: Use SCExecProxyMetaData.ABI instead. +var SCExecProxyABI = SCExecProxyMetaData.ABI + +// SCExecProxy is an auto generated Go binding around an Ethereum contract. +type SCExecProxy struct { + SCExecProxyCaller // Read-only binding to the contract + SCExecProxyTransactor // Write-only binding to the contract + SCExecProxyFilterer // Log filterer for contract events +} + +// SCExecProxyCaller is an auto generated read-only Go binding around an Ethereum contract. +type SCExecProxyCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SCExecProxyTransactor is an auto generated write-only Go binding around an Ethereum contract. +type SCExecProxyTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SCExecProxyFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type SCExecProxyFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SCExecProxySession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type SCExecProxySession struct { + Contract *SCExecProxy // 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 +} + +// SCExecProxyCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type SCExecProxyCallerSession struct { + Contract *SCExecProxyCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// SCExecProxyTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type SCExecProxyTransactorSession struct { + Contract *SCExecProxyTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SCExecProxyRaw is an auto generated low-level Go binding around an Ethereum contract. +type SCExecProxyRaw struct { + Contract *SCExecProxy // Generic contract binding to access the raw methods on +} + +// SCExecProxyCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type SCExecProxyCallerRaw struct { + Contract *SCExecProxyCaller // Generic read-only contract binding to access the raw methods on +} + +// SCExecProxyTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type SCExecProxyTransactorRaw struct { + Contract *SCExecProxyTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewSCExecProxy creates a new instance of SCExecProxy, bound to a specific deployed contract. +func NewSCExecProxy(address common.Address, backend bind.ContractBackend) (*SCExecProxy, error) { + contract, err := bindSCExecProxy(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &SCExecProxy{SCExecProxyCaller: SCExecProxyCaller{contract: contract}, SCExecProxyTransactor: SCExecProxyTransactor{contract: contract}, SCExecProxyFilterer: SCExecProxyFilterer{contract: contract}}, nil +} + +// NewSCExecProxyCaller creates a new read-only instance of SCExecProxy, bound to a specific deployed contract. +func NewSCExecProxyCaller(address common.Address, caller bind.ContractCaller) (*SCExecProxyCaller, error) { + contract, err := bindSCExecProxy(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SCExecProxyCaller{contract: contract}, nil +} + +// NewSCExecProxyTransactor creates a new write-only instance of SCExecProxy, bound to a specific deployed contract. +func NewSCExecProxyTransactor(address common.Address, transactor bind.ContractTransactor) (*SCExecProxyTransactor, error) { + contract, err := bindSCExecProxy(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SCExecProxyTransactor{contract: contract}, nil +} + +// NewSCExecProxyFilterer creates a new log filterer instance of SCExecProxy, bound to a specific deployed contract. +func NewSCExecProxyFilterer(address common.Address, filterer bind.ContractFilterer) (*SCExecProxyFilterer, error) { + contract, err := bindSCExecProxy(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SCExecProxyFilterer{contract: contract}, nil +} + +// bindSCExecProxy binds a generic wrapper to an already deployed contract. +func bindSCExecProxy(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SCExecProxyMetaData.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 (_SCExecProxy *SCExecProxyRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SCExecProxy.Contract.SCExecProxyCaller.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 (_SCExecProxy *SCExecProxyRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SCExecProxy.Contract.SCExecProxyTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SCExecProxy *SCExecProxyRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SCExecProxy.Contract.SCExecProxyTransactor.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 (_SCExecProxy *SCExecProxyCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SCExecProxy.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 (_SCExecProxy *SCExecProxyTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SCExecProxy.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SCExecProxy *SCExecProxyTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SCExecProxy.Contract.contract.Transact(opts, method, params...) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_SCExecProxy *SCExecProxyCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _SCExecProxy.contract.Call(opts, &out, "admin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_SCExecProxy *SCExecProxySession) Admin() (common.Address, error) { + return _SCExecProxy.Contract.Admin(&_SCExecProxy.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_SCExecProxy *SCExecProxyCallerSession) Admin() (common.Address, error) { + return _SCExecProxy.Contract.Admin(&_SCExecProxy.CallOpts) +} + +// IsSafePaused is a free data retrieval call binding the contract method 0xa0579640. +// +// Solidity: function isSafePaused() view returns(bool) +func (_SCExecProxy *SCExecProxyCaller) IsSafePaused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _SCExecProxy.contract.Call(opts, &out, "isSafePaused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsSafePaused is a free data retrieval call binding the contract method 0xa0579640. +// +// Solidity: function isSafePaused() view returns(bool) +func (_SCExecProxy *SCExecProxySession) IsSafePaused() (bool, error) { + return _SCExecProxy.Contract.IsSafePaused(&_SCExecProxy.CallOpts) +} + +// IsSafePaused is a free data retrieval call binding the contract method 0xa0579640. +// +// Solidity: function isSafePaused() view returns(bool) +func (_SCExecProxy *SCExecProxyCallerSession) IsSafePaused() (bool, error) { + return _SCExecProxy.Contract.IsSafePaused(&_SCExecProxy.CallOpts) +} + +// Safe is a free data retrieval call binding the contract method 0x186f0354. +// +// Solidity: function safe() view returns(address) +func (_SCExecProxy *SCExecProxyCaller) Safe(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _SCExecProxy.contract.Call(opts, &out, "safe") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Safe is a free data retrieval call binding the contract method 0x186f0354. +// +// Solidity: function safe() view returns(address) +func (_SCExecProxy *SCExecProxySession) Safe() (common.Address, error) { + return _SCExecProxy.Contract.Safe(&_SCExecProxy.CallOpts) +} + +// Safe is a free data retrieval call binding the contract method 0x186f0354. +// +// Solidity: function safe() view returns(address) +func (_SCExecProxy *SCExecProxyCallerSession) Safe() (common.Address, error) { + return _SCExecProxy.Contract.Safe(&_SCExecProxy.CallOpts) +} + +// Deposit is a paid mutator transaction binding the contract method 0xb9930981. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress, uint64 mvxGasLimit, string callData) returns() +func (_SCExecProxy *SCExecProxyTransactor) Deposit(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, mvxGasLimit uint64, callData string) (*types.Transaction, error) { + return _SCExecProxy.contract.Transact(opts, "deposit", tokenAddress, amount, recipientAddress, mvxGasLimit, callData) +} + +// Deposit is a paid mutator transaction binding the contract method 0xb9930981. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress, uint64 mvxGasLimit, string callData) returns() +func (_SCExecProxy *SCExecProxySession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, mvxGasLimit uint64, callData string) (*types.Transaction, error) { + return _SCExecProxy.Contract.Deposit(&_SCExecProxy.TransactOpts, tokenAddress, amount, recipientAddress, mvxGasLimit, callData) +} + +// Deposit is a paid mutator transaction binding the contract method 0xb9930981. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress, uint64 mvxGasLimit, string callData) returns() +func (_SCExecProxy *SCExecProxyTransactorSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, mvxGasLimit uint64, callData string) (*types.Transaction, error) { + return _SCExecProxy.Contract.Deposit(&_SCExecProxy.TransactOpts, tokenAddress, amount, recipientAddress, mvxGasLimit, callData) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_SCExecProxy *SCExecProxyTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SCExecProxy.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_SCExecProxy *SCExecProxySession) RenounceAdmin() (*types.Transaction, error) { + return _SCExecProxy.Contract.RenounceAdmin(&_SCExecProxy.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_SCExecProxy *SCExecProxyTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _SCExecProxy.Contract.RenounceAdmin(&_SCExecProxy.TransactOpts) +} + +// SetSafe is a paid mutator transaction binding the contract method 0x5db0cb94. +// +// Solidity: function setSafe(address erc20Safe) returns() +func (_SCExecProxy *SCExecProxyTransactor) SetSafe(opts *bind.TransactOpts, erc20Safe common.Address) (*types.Transaction, error) { + return _SCExecProxy.contract.Transact(opts, "setSafe", erc20Safe) +} + +// SetSafe is a paid mutator transaction binding the contract method 0x5db0cb94. +// +// Solidity: function setSafe(address erc20Safe) returns() +func (_SCExecProxy *SCExecProxySession) SetSafe(erc20Safe common.Address) (*types.Transaction, error) { + return _SCExecProxy.Contract.SetSafe(&_SCExecProxy.TransactOpts, erc20Safe) +} + +// SetSafe is a paid mutator transaction binding the contract method 0x5db0cb94. +// +// Solidity: function setSafe(address erc20Safe) returns() +func (_SCExecProxy *SCExecProxyTransactorSession) SetSafe(erc20Safe common.Address) (*types.Transaction, error) { + return _SCExecProxy.Contract.SetSafe(&_SCExecProxy.TransactOpts, erc20Safe) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_SCExecProxy *SCExecProxyTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _SCExecProxy.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_SCExecProxy *SCExecProxySession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _SCExecProxy.Contract.TransferAdmin(&_SCExecProxy.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_SCExecProxy *SCExecProxyTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _SCExecProxy.Contract.TransferAdmin(&_SCExecProxy.TransactOpts, newAdmin) +} + +// SCExecProxyAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the SCExecProxy contract. +type SCExecProxyAdminRoleTransferredIterator struct { + Event *SCExecProxyAdminRoleTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SCExecProxyAdminRoleTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SCExecProxyAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SCExecProxyAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SCExecProxyAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SCExecProxyAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SCExecProxyAdminRoleTransferred represents a AdminRoleTransferred event raised by the SCExecProxy contract. +type SCExecProxyAdminRoleTransferred struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminRoleTransferred is a free log retrieval operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_SCExecProxy *SCExecProxyFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*SCExecProxyAdminRoleTransferredIterator, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _SCExecProxy.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &SCExecProxyAdminRoleTransferredIterator{contract: _SCExecProxy.contract, event: "AdminRoleTransferred", logs: logs, sub: sub}, nil +} + +// WatchAdminRoleTransferred is a free log subscription operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_SCExecProxy *SCExecProxyFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *SCExecProxyAdminRoleTransferred, previousAdmin []common.Address, newAdmin []common.Address) (event.Subscription, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _SCExecProxy.contract.WatchLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SCExecProxyAdminRoleTransferred) + if err := _SCExecProxy.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminRoleTransferred is a log parse operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_SCExecProxy *SCExecProxyFilterer) ParseAdminRoleTransferred(log types.Log) (*SCExecProxyAdminRoleTransferred, error) { + event := new(SCExecProxyAdminRoleTransferred) + if err := _SCExecProxy.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// SCExecProxyERC20SCDepositIterator is returned from FilterERC20SCDeposit and is used to iterate over the raw logs and unpacked data for ERC20SCDeposit events raised by the SCExecProxy contract. +type SCExecProxyERC20SCDepositIterator struct { + Event *SCExecProxyERC20SCDeposit // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *SCExecProxyERC20SCDepositIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(SCExecProxyERC20SCDeposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(SCExecProxyERC20SCDeposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *SCExecProxyERC20SCDepositIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *SCExecProxyERC20SCDepositIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// SCExecProxyERC20SCDeposit represents a ERC20SCDeposit event raised by the SCExecProxy contract. +type SCExecProxyERC20SCDeposit struct { + BatchNonce uint64 + DepositNonce uint64 + MvxGasLimit uint64 + CallData string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20SCDeposit is a free log retrieval operation binding the contract event 0x4f330e92497d6cc47fffe9408a15ace7b6012d2999529e32dd9b3de825e4f748. +// +// Solidity: event ERC20SCDeposit(uint64 indexed batchNonce, uint64 depositNonce, uint64 mvxGasLimit, string callData) +func (_SCExecProxy *SCExecProxyFilterer) FilterERC20SCDeposit(opts *bind.FilterOpts, batchNonce []uint64) (*SCExecProxyERC20SCDepositIterator, error) { + + var batchNonceRule []interface{} + for _, batchNonceItem := range batchNonce { + batchNonceRule = append(batchNonceRule, batchNonceItem) + } + + logs, sub, err := _SCExecProxy.contract.FilterLogs(opts, "ERC20SCDeposit", batchNonceRule) + if err != nil { + return nil, err + } + return &SCExecProxyERC20SCDepositIterator{contract: _SCExecProxy.contract, event: "ERC20SCDeposit", logs: logs, sub: sub}, nil +} + +// WatchERC20SCDeposit is a free log subscription operation binding the contract event 0x4f330e92497d6cc47fffe9408a15ace7b6012d2999529e32dd9b3de825e4f748. +// +// Solidity: event ERC20SCDeposit(uint64 indexed batchNonce, uint64 depositNonce, uint64 mvxGasLimit, string callData) +func (_SCExecProxy *SCExecProxyFilterer) WatchERC20SCDeposit(opts *bind.WatchOpts, sink chan<- *SCExecProxyERC20SCDeposit, batchNonce []uint64) (event.Subscription, error) { + + var batchNonceRule []interface{} + for _, batchNonceItem := range batchNonce { + batchNonceRule = append(batchNonceRule, batchNonceItem) + } + + logs, sub, err := _SCExecProxy.contract.WatchLogs(opts, "ERC20SCDeposit", batchNonceRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(SCExecProxyERC20SCDeposit) + if err := _SCExecProxy.contract.UnpackLog(event, "ERC20SCDeposit", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20SCDeposit is a log parse operation binding the contract event 0x4f330e92497d6cc47fffe9408a15ace7b6012d2999529e32dd9b3de825e4f748. +// +// Solidity: event ERC20SCDeposit(uint64 indexed batchNonce, uint64 depositNonce, uint64 mvxGasLimit, string callData) +func (_SCExecProxy *SCExecProxyFilterer) ParseERC20SCDeposit(log types.Log) (*SCExecProxyERC20SCDeposit, error) { + event := new(SCExecProxyERC20SCDeposit) + if err := _SCExecProxy.contract.UnpackLog(event, "ERC20SCDeposit", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/clients/ethereum/interface.go b/clients/ethereum/interface.go index ae1d5aa9..480836a1 100644 --- a/clients/ethereum/interface.go +++ b/clients/ethereum/interface.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -28,6 +29,7 @@ type ClientWrapper interface { GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) IsPaused(ctx context.Context) (bool, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // Erc20ContractsHolder defines the Ethereum ERC20 contract operations diff --git a/clients/ethereum/wrappers/errors.go b/clients/ethereum/wrappers/errors.go index 8efee102..640bae26 100644 --- a/clients/ethereum/wrappers/errors.go +++ b/clients/ethereum/wrappers/errors.go @@ -3,7 +3,8 @@ package wrappers import "errors" var ( - errNilErc20Contract = errors.New("nil ERC20 contract") - errNilBlockchainClient = errors.New("nil blockchain client") - errNilMultiSigContract = errors.New("nil multi sig contract") + errNilErc20Contract = errors.New("nil ERC20 contract") + errNilBlockchainClient = errors.New("nil blockchain client") + errNilMultiSigContract = errors.New("nil multi sig contract") + errNilSCExecProxyContract = errors.New("nil sc exec proxy contract") ) diff --git a/clients/ethereum/wrappers/ethereumChainWrapper.go b/clients/ethereum/wrappers/ethereumChainWrapper.go index 599bf3ac..dd4ea2bf 100644 --- a/clients/ethereum/wrappers/ethereumChainWrapper.go +++ b/clients/ethereum/wrappers/ethereumChainWrapper.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -15,15 +16,17 @@ import ( // ArgsEthereumChainWrapper is the DTO used to construct a ethereumChainWrapper instance type ArgsEthereumChainWrapper struct { - StatusHandler core.StatusHandler - MultiSigContract multiSigContract - BlockchainClient blockchainClient + StatusHandler core.StatusHandler + MultiSigContract multiSigContract + SCExecProxyContract scExecProxyContract + BlockchainClient blockchainClient } type ethereumChainWrapper struct { core.StatusHandler - multiSigContract multiSigContract - blockchainClient blockchainClient + multiSigContract multiSigContract + scExecProxyContract scExecProxyContract + blockchainClient blockchainClient } // NewEthereumChainWrapper creates a new instance of type ethereumChainWrapper @@ -34,9 +37,10 @@ func NewEthereumChainWrapper(args ArgsEthereumChainWrapper) (*ethereumChainWrapp } return ðereumChainWrapper{ - StatusHandler: args.StatusHandler, - multiSigContract: args.MultiSigContract, - blockchainClient: args.BlockchainClient, + StatusHandler: args.StatusHandler, + multiSigContract: args.MultiSigContract, + scExecProxyContract: args.SCExecProxyContract, + blockchainClient: args.BlockchainClient, }, nil } @@ -47,6 +51,9 @@ func checkArgs(args ArgsEthereumChainWrapper) error { if check.IfNilReflect(args.MultiSigContract) { return errNilMultiSigContract } + if check.IfNilReflect(args.SCExecProxyContract) { + return errNilSCExecProxyContract + } if check.IfNilReflect(args.BlockchainClient) { return errNilBlockchainClient } @@ -84,6 +91,12 @@ func (wrapper *ethereumChainWrapper) ChainID(ctx context.Context) (*big.Int, err return wrapper.blockchainClient.ChainID(ctx) } +// FilterLogs executes a query and returns matching logs and events +func (wrapper *ethereumChainWrapper) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.FilterLogs(ctx, q) +} + // BlockNumber returns the current ethereum block number func (wrapper *ethereumChainWrapper) BlockNumber(ctx context.Context) (uint64, error) { wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) diff --git a/clients/ethereum/wrappers/ethereumChainWrapper_test.go b/clients/ethereum/wrappers/ethereumChainWrapper_test.go index 9473e933..e5b23611 100644 --- a/clients/ethereum/wrappers/ethereumChainWrapper_test.go +++ b/clients/ethereum/wrappers/ethereumChainWrapper_test.go @@ -4,8 +4,10 @@ import ( "context" "errors" "math/big" + "reflect" "testing" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -23,9 +25,10 @@ func createMockArgsEthereumChainWrapper() (ArgsEthereumChainWrapper, *testsCommo statusHandler := testsCommon.NewStatusHandlerMock("mock") return ArgsEthereumChainWrapper{ - MultiSigContract: &bridgeTests.MultiSigContractStub{}, - BlockchainClient: &interactors.BlockchainClientStub{}, - StatusHandler: statusHandler, + MultiSigContract: &bridgeTests.MultiSigContractStub{}, + BlockchainClient: &interactors.BlockchainClientStub{}, + SCExecProxyContract: &bridgeTests.SCExecProxyContractStub{}, + StatusHandler: statusHandler, }, statusHandler } @@ -62,6 +65,16 @@ func TestNewMultiSigContractWrapper(t *testing.T) { assert.True(t, check.IfNil(wrapper)) assert.Equal(t, errNilMultiSigContract, err) }) + t.Run("nil sc exec contract", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.MultiSigContract = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, errNilMultiSigContract, err) + }) t.Run("should work", func(t *testing.T) { t.Parallel() @@ -294,3 +307,45 @@ func TestEthereumChainWrapper_IsPaused(t *testing.T) { assert.True(t, result) assert.True(t, handlerCalled) } + +func TestEthereumChainWrapper_FilterLogs(t *testing.T) { + t.Parallel() + + expectedError := errors.New("expected error") + args, _ := createMockArgsEthereumChainWrapper() + + t.Run("returns error", func(t *testing.T) { + args.BlockchainClient = &interactors.BlockchainClientStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return nil, expectedError + }, + } + + wrapper, _ := NewEthereumChainWrapper(args) + + logs, err := wrapper.FilterLogs(context.Background(), ethereum.FilterQuery{}) + + assert.Nil(t, logs) + assert.Equal(t, expectedError, err) + }) + + t.Run("returns expected logs", func(t *testing.T) { + expectedLogs := []types.Log{ + { + Index: 1, + }, + } + args.BlockchainClient = &interactors.BlockchainClientStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return expectedLogs, nil + }, + } + + wrapper, _ := NewEthereumChainWrapper(args) + logs, err := wrapper.FilterLogs(context.Background(), ethereum.FilterQuery{}) + + assert.Nil(t, err) + assert.True(t, reflect.DeepEqual(expectedLogs, logs)) + }) + +} diff --git a/clients/ethereum/wrappers/interface.go b/clients/ethereum/wrappers/interface.go index 6537ed82..0053a627 100644 --- a/clients/ethereum/wrappers/interface.go +++ b/clients/ethereum/wrappers/interface.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -25,9 +26,13 @@ type multiSigContract interface { Paused(opts *bind.CallOpts) (bool, error) } +type scExecProxyContract interface { +} + type blockchainClient interface { BlockNumber(ctx context.Context) (uint64, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) ChainID(ctx context.Context) (*big.Int, error) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } diff --git a/clients/multiversx/client.go b/clients/multiversx/client.go index ae940d80..ac1864c1 100644 --- a/clients/multiversx/client.go +++ b/clients/multiversx/client.go @@ -310,6 +310,10 @@ func (c *client) ProposeTransfer(ctx context.Context, batch *clients.TransferBat ArgBytes(dt.ConvertedTokenBytes). ArgBigInt(dt.Amount). ArgInt64(int64(dt.Nonce)) + if len(dt.Data) > 0 { + txBuilder.ArgBytes(dt.Data). + ArgInt64(int64(dt.ExtraGasLimit)) + } } gasLimit := c.gasMapConfig.ProposeTransferBase + uint64(len(batch.Deposits))*c.gasMapConfig.ProposeTransferForEach diff --git a/cmd/bridge/config/config.toml b/cmd/bridge/config/config.toml index 8228d852..03b39877 100644 --- a/cmd/bridge/config/config.toml +++ b/cmd/bridge/config/config.toml @@ -3,6 +3,7 @@ NetworkAddress = "http://127.0.0.1:8545" # a network address MultisigContractAddress = "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c" # the eth address for the bridge contract SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + SCExecProxyAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" # eth address for bridge sc execution logic PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key GasLimitBase = 350000 GasLimitForEach = 30000 diff --git a/cmd/bridge/main.go b/cmd/bridge/main.go index 6bb2083f..322daec9 100644 --- a/cmd/bridge/main.go +++ b/cmd/bridge/main.go @@ -184,6 +184,12 @@ func startRelay(ctx *cli.Context, version string) error { return err } + scExecProxyAddr := ethCommon.HexToAddress(cfg.Eth.SCExecProxyAddress) + scExecProxy, err := contract.NewSCExecProxy(scExecProxyAddr, ethClient) + if err != nil { + return err + } + argsContractsHolder := ethereum.ArgsErc20SafeContractsHolder{ EthClient: ethClient, EthClientStatusHandler: ethClientStatusHandler, @@ -210,9 +216,10 @@ func startRelay(ctx *cli.Context, version string) error { } argsClientWrapper := wrappers.ArgsEthereumChainWrapper{ - StatusHandler: ethClientStatusHandler, - MultiSigContract: multiSigInstance, - BlockchainClient: ethClient, + StatusHandler: ethClientStatusHandler, + MultiSigContract: multiSigInstance, + BlockchainClient: ethClient, + SCExecProxyContract: scExecProxy, } clientWrapper, err := wrappers.NewEthereumChainWrapper(argsClientWrapper) diff --git a/config/config.go b/config/config.go index 0c3cf741..e14326d4 100644 --- a/config/config.go +++ b/config/config.go @@ -32,6 +32,7 @@ type EthereumConfig struct { NetworkAddress string MultisigContractAddress string SafeContractAddress string + SCExecProxyAddress string PrivateKeyFile string IntervalToResendTxsInSeconds uint64 GasLimitBase uint64 diff --git a/factory/ethMultiversXBridgeComponents.go b/factory/ethMultiversXBridgeComponents.go index 37970c74..09a67111 100644 --- a/factory/ethMultiversXBridgeComponents.go +++ b/factory/ethMultiversXBridgeComponents.go @@ -387,6 +387,7 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE } safeContractAddress := common.HexToAddress(ethereumConfigs.SafeContractAddress) + scExecProxyAddress := common.HexToAddress(ethereumConfigs.SCExecProxyAddress) ethClientLogId := components.evmCompatibleChain.EvmCompatibleChainClientLogId() argsEthClient := ethereum.ArgsEthereumClient{ @@ -399,6 +400,7 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE TokensMapper: tokensMapper, SignatureHolder: signaturesHolder, SafeContractAddress: safeContractAddress, + SCExecProxyAddress: scExecProxyAddress, GasHandler: gs, TransferGasLimitBase: ethereumConfigs.GasLimitBase, TransferGasLimitForEach: ethereumConfigs.GasLimitForEach, diff --git a/go.mod b/go.mod index bcf3ff34..7826cab9 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/multiversx/mx-chain-logger-go v1.0.11 github.com/multiversx/mx-chain-p2p-go v1.0.18 github.com/multiversx/mx-sdk-go v1.3.6-rc1 + github.com/pelletier/go-toml v1.9.3 github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.10 ) @@ -120,7 +121,6 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/integrationTests/mock/ethereumChainMock.go b/integrationTests/mock/ethereumChainMock.go index f47dafee..6300f4dc 100644 --- a/integrationTests/mock/ethereumChainMock.go +++ b/integrationTests/mock/ethereumChainMock.go @@ -6,6 +6,7 @@ import ( "math/big" "sync" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -37,6 +38,7 @@ type EthereumChainMock struct { relayers []common.Address ProposeMultiTransferEsdtBatchCalled func() BalanceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // NewEthereumChainMock - @@ -252,6 +254,15 @@ func (mock *EthereumChainMock) BalanceAt(ctx context.Context, account common.Add return big.NewInt(0), nil } +// FilterLogs - +func (mock *EthereumChainMock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if mock.FilterLogsCalled != nil { + return mock.FilterLogsCalled(ctx, q) + } + + return []types.Log{}, nil +} + // IsPaused - func (mock *EthereumChainMock) IsPaused(_ context.Context) (bool, error) { return false, nil diff --git a/testsCommon/bridge/ethereumClientStub.go b/testsCommon/bridge/ethereumClientStub.go index f715fc95..afa84691 100644 --- a/testsCommon/bridge/ethereumClientStub.go +++ b/testsCommon/bridge/ethereumClientStub.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" ) // EthereumClientStub - @@ -19,6 +20,8 @@ type EthereumClientStub struct { GetTransactionsStatusesCalled func(ctx context.Context, batchId uint64) ([]byte, error) GetQuorumSizeCalled func(ctx context.Context) (*big.Int, error) IsQuorumReachedCalled func(ctx context.Context, msgHash common.Hash) (bool, error) + IsDepositSCCallCalled func(deposit *clients.DepositTransfer) bool + GetBatchSCMetadataCalled func(ctx context.Context, nonce uint64) ([]*contract.SCExecProxyERC20SCDeposit, error) } // GetBatch - @@ -100,6 +103,24 @@ func (stub *EthereumClientStub) IsQuorumReached(ctx context.Context, msgHash com return false, errNotImplemented } +// IsDepositSCCall - +func (stub *EthereumClientStub) IsDepositSCCall(deposit *clients.DepositTransfer) bool { + if stub.IsDepositSCCallCalled != nil { + return stub.IsDepositSCCallCalled(deposit) + } + + return false +} + +// GetBatchSCMetadata - +func (stub *EthereumClientStub) GetBatchSCMetadata(ctx context.Context, nonce uint64) ([]*contract.SCExecProxyERC20SCDeposit, error) { + if stub.GetBatchSCMetadataCalled != nil { + return stub.GetBatchSCMetadataCalled(ctx, nonce) + } + + return []*contract.SCExecProxyERC20SCDeposit{}, nil +} + // IsInterfaceNil - func (stub *EthereumClientStub) IsInterfaceNil() bool { return stub == nil diff --git a/testsCommon/bridge/ethereumClientWrapperStub.go b/testsCommon/bridge/ethereumClientWrapperStub.go index 1a476500..97fb6b06 100644 --- a/testsCommon/bridge/ethereumClientWrapperStub.go +++ b/testsCommon/bridge/ethereumClientWrapperStub.go @@ -5,6 +5,7 @@ import ( "errors" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -34,6 +35,7 @@ type EthereumClientWrapperStub struct { GetAllMetricsCalled func() core.GeneralMetrics NameCalled func() string IsPausedCalled func(ctx context.Context) (bool, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // SetIntMetric - @@ -189,6 +191,15 @@ func (stub *EthereumClientWrapperStub) BalanceAt(ctx context.Context, account co return big.NewInt(0), nil } +// FilterLogs - +func (stub *EthereumClientWrapperStub) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if stub.FilterLogsCalled != nil { + return stub.FilterLogsCalled(ctx, q) + } + + return []types.Log{}, nil +} + // IsPaused - func (stub *EthereumClientWrapperStub) IsPaused(ctx context.Context) (bool, error) { if stub.IsPausedCalled != nil { diff --git a/testsCommon/bridge/scExecProxyContractStub.go b/testsCommon/bridge/scExecProxyContractStub.go new file mode 100644 index 00000000..60fec043 --- /dev/null +++ b/testsCommon/bridge/scExecProxyContractStub.go @@ -0,0 +1,22 @@ +package bridge + +import ( + "context" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/core/types" +) + +// SCExecProxyContractStub - +type SCExecProxyContractStub struct { + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) +} + +// FilterLogs - +func (stub *SCExecProxyContractStub) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if stub.FilterLogsCalled != nil { + return stub.FilterLogsCalled(ctx, q) + } + + return []types.Log{}, nil +} diff --git a/testsCommon/interactors/blockchainClientStub.go b/testsCommon/interactors/blockchainClientStub.go index 53cfd3f6..81de954e 100644 --- a/testsCommon/interactors/blockchainClientStub.go +++ b/testsCommon/interactors/blockchainClientStub.go @@ -4,7 +4,9 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ) // BlockchainClientStub - @@ -13,6 +15,7 @@ type BlockchainClientStub struct { NonceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) ChainIDCalled func(ctx context.Context) (*big.Int, error) BalanceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // BlockNumber - @@ -51,6 +54,15 @@ func (bcs *BlockchainClientStub) BalanceAt(ctx context.Context, account common.A return big.NewInt(0), nil } +// FilterLogs - +func (bcs *BlockchainClientStub) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if bcs.FilterLogsCalled != nil { + return bcs.FilterLogsCalled(ctx, q) + } + + return nil, nil +} + // IsInterfaceNil returns true if there is no value under the interface func (bcs *BlockchainClientStub) IsInterfaceNil() bool { return bcs == nil