From e76d3c98bbe473ba0c9bee772bbfcd43b72c4e0d Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 2 Sep 2024 09:54:22 +0300 Subject: [PATCH 01/15] - added migration batch creator components - added migration tool boiler plate code --- cmd/migration/config/config.toml | 50 + cmd/migration/config/migration.json | 19 + cmd/migration/disabled/statusHandler.go | 34 + cmd/migration/flags.go | 46 + cmd/migration/keys/ethereum.sk | 1 + cmd/migration/main.go | 176 ++ config/config.go | 13 + config/tomlConfigs_test.go | 116 ++ .../bridgeV2Wrappers/ERC20Safe.abi.json | 763 ++++++++ .../ethereum/bridgeV2Wrappers/ERC20Safe.go | 1654 +++++++++++++++++ executors/ethereum/common.go | 25 + executors/ethereum/errors.go | 9 + executors/ethereum/interface.go | 27 + executors/ethereum/migrationBatchCreator.go | 130 ++ 14 files changed, 3063 insertions(+) create mode 100644 cmd/migration/config/config.toml create mode 100644 cmd/migration/config/migration.json create mode 100644 cmd/migration/disabled/statusHandler.go create mode 100644 cmd/migration/flags.go create mode 100644 cmd/migration/keys/ethereum.sk create mode 100644 cmd/migration/main.go create mode 100644 executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json create mode 100644 executors/ethereum/bridgeV2Wrappers/ERC20Safe.go create mode 100644 executors/ethereum/common.go create mode 100644 executors/ethereum/errors.go create mode 100644 executors/ethereum/interface.go create mode 100644 executors/ethereum/migrationBatchCreator.go diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml new file mode 100644 index 00000000..77cfefc0 --- /dev/null +++ b/cmd/migration/config/config.toml @@ -0,0 +1,50 @@ +[Eth] + Chain = "Ethereum" + NetworkAddress = "http://127.0.0.1:8545" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "observer" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[WhitelistedTokens] + List = [ + "ETHUSDC-220753", + "ETHUTK-8cdf7a", + "ETHUSDT-9c73c6", + "ETHBUSD-450923", + "ETHHMT-18538a", + "ETHCGG-ee4e0c", + "ETHINFRA-60a3bf", + "ETHWBTC-74e282", + "ETHWETH-e1c126", + "ETHWSDAI-572803", + "ETHWDAI-bd65f9", + "ETHUMB-291202", + ] diff --git a/cmd/migration/config/migration.json b/cmd/migration/config/migration.json new file mode 100644 index 00000000..4698c37e --- /dev/null +++ b/cmd/migration/config/migration.json @@ -0,0 +1,19 @@ +{ + "OldSafeContractAddress": "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + "NewSafeContractAddress": "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", + "BatchID": 22222, + "DepositsInfo": [ + { + "DepositNonce": 1, + "Token": "USDC", + "ContractAddress": "ssss", + "Amount": "11111111111" + }, + { + "DepositNonce": 2, + "Token": "MEME", + "ContractAddress": "ssss", + "Amount": "2222222" + } + ] +} \ No newline at end of file diff --git a/cmd/migration/disabled/statusHandler.go b/cmd/migration/disabled/statusHandler.go new file mode 100644 index 00000000..7949c9cb --- /dev/null +++ b/cmd/migration/disabled/statusHandler.go @@ -0,0 +1,34 @@ +package disabled + +import "github.com/multiversx/mx-bridge-eth-go/core" + +// StatusHandler represents the disabled status handler implementation +type StatusHandler struct { +} + +// SetIntMetric does nothing +func (handler *StatusHandler) SetIntMetric(_ string, _ int) { +} + +// AddIntMetric does nothing +func (handler *StatusHandler) AddIntMetric(_ string, _ int) { +} + +// SetStringMetric does nothing +func (handler *StatusHandler) SetStringMetric(_ string, _ string) { +} + +// Name returns an empty string +func (handler *StatusHandler) Name() string { + return "" +} + +// GetAllMetrics returns an empty map +func (handler *StatusHandler) GetAllMetrics() core.GeneralMetrics { + return make(core.GeneralMetrics) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (handler *StatusHandler) IsInterfaceNil() bool { + return handler == nil +} diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go new file mode 100644 index 00000000..2afce5a0 --- /dev/null +++ b/cmd/migration/flags.go @@ -0,0 +1,46 @@ +package main + +import ( + "github.com/multiversx/mx-bridge-eth-go/config" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/urfave/cli" +) + +var ( + logLevel = cli.StringFlag{ + Name: "log-level", + Usage: "This flag specifies the logger `level(s)`. It can contain multiple comma-separated value. For example" + + ", if set to *:INFO the logs for all packages will have the INFO level. However, if set to *:INFO,api:DEBUG" + + " the logs for all packages will have the INFO level, excepting the api package which will receive a DEBUG" + + " log level.", + Value: "*:" + logger.LogDebug.String(), + } + // configurationFile defines a flag for the path to the main toml configuration file + configurationFile = cli.StringFlag{ + Name: "config", + Usage: "The `" + filePathPlaceholder + "` for the main configuration file. This TOML file contain the main " + + "configurations such as storage setups, epoch duration and so on.", + Value: "config/config.toml", + } + mode = cli.StringFlag{ + Name: "mode", + Usage: "This flag specifies the operation mode. Usage: generate, sign or execute", + Value: generateMode, + } +) + +func getFlags() []cli.Flag { + return []cli.Flag{ + logLevel, + configurationFile, + mode, + } +} +func getFlagsConfig(ctx *cli.Context) config.ContextFlagsConfig { + flagsConfig := config.ContextFlagsConfig{} + + flagsConfig.LogLevel = ctx.GlobalString(logLevel.Name) + flagsConfig.ConfigurationFile = ctx.GlobalString(configurationFile.Name) + + return flagsConfig +} diff --git a/cmd/migration/keys/ethereum.sk b/cmd/migration/keys/ethereum.sk new file mode 100644 index 00000000..5675c4b6 --- /dev/null +++ b/cmd/migration/keys/ethereum.sk @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1 diff --git a/cmd/migration/main.go b/cmd/migration/main.go new file mode 100644 index 00000000..9efb7687 --- /dev/null +++ b/cmd/migration/main.go @@ -0,0 +1,176 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" + "github.com/multiversx/mx-bridge-eth-go/clients/multiversx/mappers" + "github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" + chainCore "github.com/multiversx/mx-chain-core-go/core" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/blockchain" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" + "github.com/urfave/cli" +) + +const ( + filePathPlaceholder = "[path]" + generateMode = "generate" + signMode = "sign" + executeMode = "execute" +) + +var log = logger.GetOrCreate("main") + +func main() { + app := cli.NewApp() + app.Name = "Funds migration CLI tool" + app.Usage = "This is the entry point for the migration CLI tool" + app.Flags = getFlags() + app.Authors = []cli.Author{ + { + Name: "The MultiversX Team", + Email: "contact@multiversx.com", + }, + } + + app.Action = func(c *cli.Context) error { + return execute(c) + } + + err := app.Run(os.Args) + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } +} + +func execute(ctx *cli.Context) error { + flagsConfig := getFlagsConfig(ctx) + + err := logger.SetLogLevel(flagsConfig.LogLevel) + if err != nil { + return err + } + + cfg, err := loadConfig(flagsConfig.ConfigurationFile) + if err != nil { + return err + } + + log.Info("starting migration help tool", "pid", os.Getpid()) + + operationMode := strings.ToLower(ctx.GlobalString(mode.Name)) + switch operationMode { + case generateMode: + return generate(cfg) + case signMode: + //TODO: implement + case executeMode: + //TODO: implement + } + + return fmt.Errorf("unknown execution mode: %s", operationMode) +} + +func generate(cfg config.MigrationToolConfig) error { + argsProxy := blockchain.ArgsProxy{ + ProxyURL: cfg.MultiversX.NetworkAddress, + SameScState: false, + ShouldBeSynced: false, + FinalityCheck: cfg.MultiversX.Proxy.FinalityCheck, + AllowedDeltaToFinal: cfg.MultiversX.Proxy.MaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(cfg.MultiversX.Proxy.CacherExpirationSeconds), + EntityType: sdkCore.RestAPIEntityType(cfg.MultiversX.Proxy.RestAPIEntityType), + } + proxy, err := blockchain.NewProxy(argsProxy) + if err != nil { + return err + } + + emptyAddress := data.NewAddressFromBytes(make([]byte, 0)) + safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress) + if err != nil { + return err + } + + argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ + MultisigContractAddress: emptyAddress, + SafeContractAddress: safeAddress, + RelayerAddress: emptyAddress, + Proxy: proxy, + Log: log, + } + mxDataGetter, err := multiversx.NewMXClientDataGetter(argsMXClientDataGetter) + if err != nil { + return err + } + + tokensWrapper, err := mappers.NewMultiversXToErc20Mapper(mxDataGetter) + if err != nil { + return err + } + + ethClient, err := ethclient.Dial(cfg.Eth.NetworkAddress) + if err != nil { + return err + } + + argsContractsHolder := ethereumClient.ArgsErc20SafeContractsHolder{ + EthClient: ethClient, + EthClientStatusHandler: &disabled.StatusHandler{}, + } + erc20ContractsHolder, err := ethereumClient.NewErc20SafeContractsHolder(argsContractsHolder) + if err != nil { + return err + } + + argsCreator := ethereum.ArgsMigrationBatchCreator{ + TokensList: cfg.WhitelistedTokens.List, + TokensMapper: tokensWrapper, + Erc20ContractsHolder: erc20ContractsHolder, + SafeContractAddress: common.Address{}, + SafeContractWrapper: nil, + } + + creator, err := ethereum.NewMigrationBatchCreator(argsCreator) + if err != nil { + return err + } + + batchInfo, err := creator.CreateBatchInfo(context.Background()) + if err != nil { + return err + } + + //TODO: save in a file + val, err := json.MarshalIndent(batchInfo, "", " ") + if err != nil { + return err + } + + log.Info(string(val)) + + return nil +} + +func loadConfig(filepath string) (config.MigrationToolConfig, error) { + cfg := config.MigrationToolConfig{} + err := chainCore.LoadTomlFile(&cfg, filepath) + if err != nil { + return config.MigrationToolConfig{}, err + } + + return cfg, nil +} diff --git a/config/config.go b/config/config.go index 1976d3d0..916126db 100644 --- a/config/config.go +++ b/config/config.go @@ -205,3 +205,16 @@ type ScCallsModuleConfig struct { FilterConfig PendingOperationsFilterConfig Logs LogsConfig } + +// WhitelistedTokensConfig will hold the whitelisted tokens config +type WhitelistedTokensConfig struct { + List []string +} + +// MigrationToolConfig is the migration tool config struct +type MigrationToolConfig struct { + Eth EthereumConfig + MultiversX MultiversXConfig + Logs LogsConfig + WhitelistedTokens WhitelistedTokensConfig +} diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go index 7163fd9d..30f79d3b 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -456,3 +456,119 @@ PollingIntervalInMillis = 6000 require.Equal(t, expectedConfig, cfg) } + +func TestMigrationToolConfig(t *testing.T) { + t.Parallel() + + expectedConfig := MigrationToolConfig{ + Eth: EthereumConfig{ + Chain: "Ethereum", + NetworkAddress: "http://127.0.0.1:8545", + SafeContractAddress: "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + PrivateKeyFile: "keys/ethereum.sk", + GasLimitBase: 350000, + GasLimitForEach: 30000, + GasStation: GasStationConfig{ + Enabled: true, + URL: "https://api.etherscan.io/api?module=gastracker&action=gasoracle", + PollingIntervalInSeconds: 60, + RequestRetryDelayInSeconds: 5, + MaxFetchRetries: 3, + RequestTimeInSeconds: 2, + MaximumAllowedGasPrice: 300, + GasPriceSelector: "SafeGasPrice", + GasPriceMultiplier: 1000000000, + }, + }, + MultiversX: MultiversXConfig{ + NetworkAddress: "https://devnet-gateway.multiversx.com", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", + Proxy: ProxyConfig{ + CacherExpirationSeconds: 600, + RestAPIEntityType: "observer", + MaxNoncesDelta: 7, + FinalityCheck: true, + }, + }, + Logs: LogsConfig{ + LogFileLifeSpanInSec: 86400, + LogFileLifeSpanInMB: 1024, + }, + WhitelistedTokens: WhitelistedTokensConfig{ + List: []string{ + "ETHUSDC-220753", + "ETHUTK-8cdf7a", + "ETHUSDT-9c73c6", + "ETHBUSD-450923", + "ETHHMT-18538a", + "ETHCGG-ee4e0c", + "ETHINFRA-60a3bf", + "ETHWBTC-74e282", + "ETHWETH-e1c126", + "ETHWSDAI-572803", + "ETHWDAI-bd65f9", + "ETHUMB-291202", + }, + }, + } + + testString := ` +[Eth] + Chain = "Ethereum" + NetworkAddress = "http://127.0.0.1:8545" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "observer" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[WhitelistedTokens] + List = [ + "ETHUSDC-220753", + "ETHUTK-8cdf7a", + "ETHUSDT-9c73c6", + "ETHBUSD-450923", + "ETHHMT-18538a", + "ETHCGG-ee4e0c", + "ETHINFRA-60a3bf", + "ETHWBTC-74e282", + "ETHWETH-e1c126", + "ETHWSDAI-572803", + "ETHWDAI-bd65f9", + "ETHUMB-291202", + ] +` + + cfg := MigrationToolConfig{} + + err := toml.Unmarshal([]byte(testString), &cfg) + + require.Nil(t, err) + require.Equal(t, expectedConfig, cfg) +} diff --git a/executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json b/executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json new file mode 100644 index 00000000..81126a5e --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json @@ -0,0 +1,763 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "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": "address", + "name": "previousBridge", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "BridgeTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "depositNonce", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "batchId", + "type": "uint112" + } + ], + "name": "ERC20Deposit", + "type": "event" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchBlockLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batchDeposits", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSize", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batches", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchesCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridge", + "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" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositsCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMaxLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMinLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "initSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isAnyBatchInProgress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "isTokenWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "recoverLostFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "removeTokenFromWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchBlockLimit", + "type": "uint8" + } + ], + "name": "setBatchBlockLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "newBatchSize", + "type": "uint16" + } + ], + "name": "setBatchSize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "setBridge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMaxLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMinLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMaxLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMinLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipientAddress", + "type": "address" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximumAmount", + "type": "uint256" + } + ], + "name": "whitelistToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/executors/ethereum/bridgeV2Wrappers/ERC20Safe.go b/executors/ethereum/bridgeV2Wrappers/ERC20Safe.go new file mode 100644 index 00000000..548d35f1 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ERC20Safe.go @@ -0,0 +1,1654 @@ +// 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 +) + +// Batch is an auto generated low-level Go binding around an user-defined struct. +type Batch struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +} + +// Deposit is an auto generated low-level Go binding around an user-defined struct. +type Deposit struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +} + +// ERC20SafeMetaData contains all meta data concerning the ERC20Safe contract. +var ERC20SafeMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"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\":\"address\",\"name\":\"previousBridge\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"BridgeTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"depositNonce\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"batchId\",\"type\":\"uint112\"}],\"name\":\"ERC20Deposit\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchBlockLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batchDeposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSize\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batches\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchesCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridge\",\"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\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositsCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMaxLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMinLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initSupply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isAnyBatchInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isTokenWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"recoverLostFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeTokenFromWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchBlockLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchBlockLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newBatchSize\",\"type\":\"uint16\"}],\"name\":\"setBatchSize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"setBridge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMaxLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMinLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMaxLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMinLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minimumAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maximumAmount\",\"type\":\"uint256\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistedTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// ERC20SafeABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC20SafeMetaData.ABI instead. +var ERC20SafeABI = ERC20SafeMetaData.ABI + +// ERC20Safe is an auto generated Go binding around an Ethereum contract. +type ERC20Safe struct { + ERC20SafeCaller // Read-only binding to the contract + ERC20SafeTransactor // Write-only binding to the contract + ERC20SafeFilterer // Log filterer for contract events +} + +// ERC20SafeCaller is an auto generated read-only Go binding around an Ethereum contract. +type ERC20SafeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC20SafeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC20SafeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC20SafeSession struct { + Contract *ERC20Safe // 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 +} + +// ERC20SafeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC20SafeCallerSession struct { + Contract *ERC20SafeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC20SafeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC20SafeTransactorSession struct { + Contract *ERC20SafeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20SafeRaw is an auto generated low-level Go binding around an Ethereum contract. +type ERC20SafeRaw struct { + Contract *ERC20Safe // Generic contract binding to access the raw methods on +} + +// ERC20SafeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC20SafeCallerRaw struct { + Contract *ERC20SafeCaller // Generic read-only contract binding to access the raw methods on +} + +// ERC20SafeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC20SafeTransactorRaw struct { + Contract *ERC20SafeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC20Safe creates a new instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20Safe(address common.Address, backend bind.ContractBackend) (*ERC20Safe, error) { + contract, err := bindERC20Safe(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC20Safe{ERC20SafeCaller: ERC20SafeCaller{contract: contract}, ERC20SafeTransactor: ERC20SafeTransactor{contract: contract}, ERC20SafeFilterer: ERC20SafeFilterer{contract: contract}}, nil +} + +// NewERC20SafeCaller creates a new read-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeCaller(address common.Address, caller bind.ContractCaller) (*ERC20SafeCaller, error) { + contract, err := bindERC20Safe(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC20SafeCaller{contract: contract}, nil +} + +// NewERC20SafeTransactor creates a new write-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeTransactor(address common.Address, transactor bind.ContractTransactor) (*ERC20SafeTransactor, error) { + contract, err := bindERC20Safe(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC20SafeTransactor{contract: contract}, nil +} + +// NewERC20SafeFilterer creates a new log filterer instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeFilterer(address common.Address, filterer bind.ContractFilterer) (*ERC20SafeFilterer, error) { + contract, err := bindERC20Safe(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC20SafeFilterer{contract: contract}, nil +} + +// bindERC20Safe binds a generic wrapper to an already deployed contract. +func bindERC20Safe(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC20SafeMetaData.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 (_ERC20Safe *ERC20SafeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.Contract.ERC20SafeCaller.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 (_ERC20Safe *ERC20SafeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.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 (_ERC20Safe *ERC20SafeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.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 (_ERC20Safe *ERC20SafeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.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 (_ERC20Safe *ERC20SafeCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.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 (_ERC20Safe *ERC20SafeSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchBlockLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchBlockLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCaller) BatchDeposits(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchDeposits", arg0, arg1) + + outstruct := new(struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.TokenAddress = *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + outstruct.Amount = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.Depositor = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + outstruct.Recipient = *abi.ConvertType(out[4], new([32]byte)).(*[32]byte) + outstruct.Status = *abi.ConvertType(out[5], new(uint8)).(*uint8) + + return *outstruct, err + +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCallerSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchSettleLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSettleLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCaller) BatchSize(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSize") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCaller) Batches(opts *bind.CallOpts, arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batches", arg0) + + outstruct := new(struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint64)).(*uint64) + outstruct.LastUpdatedBlockNumber = *abi.ConvertType(out[2], new(uint64)).(*uint64) + outstruct.DepositsCount = *abi.ConvertType(out[3], new(uint16)).(*uint16) + + return *outstruct, err + +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCallerSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) BatchesCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchesCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCaller) Bridge(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "bridge") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) DepositsCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "depositsCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeCaller) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (Batch, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getBatch", batchNonce) + + if err != nil { + return *new(Batch), err + } + + out0 := *abi.ConvertType(out[0], new(Batch)).(*Batch) + + return out0, err + +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeCallerSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeCaller) GetDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]Deposit, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getDeposits", batchNonce) + + if err != nil { + return *new([]Deposit), err + } + + out0 := *abi.ConvertType(out[0], new([]Deposit)).(*[]Deposit) + + return out0, err + +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeSession) GetDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeCallerSession) GetDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMaxLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMaxLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMinLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMinLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsAnyBatchInProgress(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isAnyBatchInProgress") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsTokenWhitelisted(opts *bind.CallOpts, token common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isTokenWhitelisted", token) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) Paused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "paused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenBalances", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenBalances(&_ERC20Safe.CallOpts, arg0) +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenBalances(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMaxLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMaxLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMinLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMinLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) WhitelistedTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "whitelistedTokens", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) Deposit(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "deposit", tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) InitSupply(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "initSupply", tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "pause") +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) RecoverLostFunds(opts *bind.TransactOpts, tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "recoverLostFunds", tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactor) RemoveTokenFromWhitelist(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "removeTokenFromWhitelist", token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchBlockLimit(opts *bind.TransactOpts, newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchBlockLimit", newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSize(opts *bind.TransactOpts, newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSize", newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBridge(opts *bind.TransactOpts, newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBridge", newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMaxLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMaxLimit", token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMinLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMinLimit", token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactor) Transfer(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transfer", tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactorSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "unpause") +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeTransactor) WhitelistToken(opts *bind.TransactOpts, token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "whitelistToken", token, minimumAmount, maximumAmount) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount) +} + +// ERC20SafeAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferredIterator struct { + Event *ERC20SafeAdminRoleTransferred // 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 *ERC20SafeAdminRoleTransferredIterator) 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(ERC20SafeAdminRoleTransferred) + 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(ERC20SafeAdminRoleTransferred) + 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 *ERC20SafeAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeAdminRoleTransferred represents a AdminRoleTransferred event raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferred 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 (_ERC20Safe *ERC20SafeFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*ERC20SafeAdminRoleTransferredIterator, 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 := _ERC20Safe.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &ERC20SafeAdminRoleTransferredIterator{contract: _ERC20Safe.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 (_ERC20Safe *ERC20SafeFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeAdminRoleTransferred, 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 := _ERC20Safe.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(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.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 (_ERC20Safe *ERC20SafeFilterer) ParseAdminRoleTransferred(log types.Log) (*ERC20SafeAdminRoleTransferred, error) { + event := new(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeBridgeTransferredIterator is returned from FilterBridgeTransferred and is used to iterate over the raw logs and unpacked data for BridgeTransferred events raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferredIterator struct { + Event *ERC20SafeBridgeTransferred // 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 *ERC20SafeBridgeTransferredIterator) 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(ERC20SafeBridgeTransferred) + 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(ERC20SafeBridgeTransferred) + 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 *ERC20SafeBridgeTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeBridgeTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeBridgeTransferred represents a BridgeTransferred event raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferred struct { + PreviousBridge common.Address + NewBridge common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBridgeTransferred is a free log retrieval operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) FilterBridgeTransferred(opts *bind.FilterOpts, previousBridge []common.Address, newBridge []common.Address) (*ERC20SafeBridgeTransferredIterator, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + if err != nil { + return nil, err + } + return &ERC20SafeBridgeTransferredIterator{contract: _ERC20Safe.contract, event: "BridgeTransferred", logs: logs, sub: sub}, nil +} + +// WatchBridgeTransferred is a free log subscription operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) WatchBridgeTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeBridgeTransferred, previousBridge []common.Address, newBridge []common.Address) (event.Subscription, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + 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(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", 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 +} + +// ParseBridgeTransferred is a log parse operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) ParseBridgeTransferred(log types.Log) (*ERC20SafeBridgeTransferred, error) { + event := new(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeERC20DepositIterator is returned from FilterERC20Deposit and is used to iterate over the raw logs and unpacked data for ERC20Deposit events raised by the ERC20Safe contract. +type ERC20SafeERC20DepositIterator struct { + Event *ERC20SafeERC20Deposit // 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 *ERC20SafeERC20DepositIterator) 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(ERC20SafeERC20Deposit) + 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(ERC20SafeERC20Deposit) + 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 *ERC20SafeERC20DepositIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeERC20DepositIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeERC20Deposit represents a ERC20Deposit event raised by the ERC20Safe contract. +type ERC20SafeERC20Deposit struct { + DepositNonce *big.Int + BatchId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20Deposit is a free log retrieval operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) FilterERC20Deposit(opts *bind.FilterOpts) (*ERC20SafeERC20DepositIterator, error) { + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "ERC20Deposit") + if err != nil { + return nil, err + } + return &ERC20SafeERC20DepositIterator{contract: _ERC20Safe.contract, event: "ERC20Deposit", logs: logs, sub: sub}, nil +} + +// WatchERC20Deposit is a free log subscription operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) WatchERC20Deposit(opts *bind.WatchOpts, sink chan<- *ERC20SafeERC20Deposit) (event.Subscription, error) { + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "ERC20Deposit") + 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(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", 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 +} + +// ParseERC20Deposit is a log parse operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) ParseERC20Deposit(log types.Log) (*ERC20SafeERC20Deposit, error) { + event := new(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go new file mode 100644 index 00000000..71f5e146 --- /dev/null +++ b/executors/ethereum/common.go @@ -0,0 +1,25 @@ +package ethereum + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// DepositInfo is the deposit info list +type DepositInfo struct { + DepositNonce uint64 `json:"DepositNonce"` + Token string `json:"Token"` + ContractAddress string `json:"ContractAddress"` + contractAddress common.Address + amount *big.Int + Amount string `json:"Amount"` +} + +// BatchInfo is the batch info list +type BatchInfo struct { + OldSafeContractAddress string `json:"OldSafeContractAddress"` + NewSafeContractAddress string `json:"NewSafeContractAddress"` + BatchID uint64 `json:"BatchID"` + DepositsInfo []*DepositInfo `json:"DepositsInfo"` +} diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go new file mode 100644 index 00000000..3bb07bac --- /dev/null +++ b/executors/ethereum/errors.go @@ -0,0 +1,9 @@ +package ethereum + +import "errors" + +var ( + errEmptyTokensList = errors.New("empty tokens list") + errNilTokensMapper = errors.New("nil MultiversX to Ethereum tokens mapper") + errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") +) diff --git a/executors/ethereum/interface.go b/executors/ethereum/interface.go new file mode 100644 index 00000000..5ddaf025 --- /dev/null +++ b/executors/ethereum/interface.go @@ -0,0 +1,27 @@ +package ethereum + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" +) + +// TokensMapper can convert a token bytes from one chain to another +type TokensMapper interface { + ConvertToken(ctx context.Context, sourceBytes []byte) ([]byte, error) + IsInterfaceNil() bool +} + +// Erc20ContractsHolder defines the Ethereum ERC20 contract operations +type Erc20ContractsHolder interface { + BalanceOf(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) + IsInterfaceNil() bool +} + +// SafeContractWrapper defines the operations for the safe contract +type SafeContractWrapper interface { + DepositsCount(opts *bind.CallOpts) (uint64, error) + BatchesCount(opts *bind.CallOpts) (uint64, error) +} diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go new file mode 100644 index 00000000..c208c799 --- /dev/null +++ b/executors/ethereum/migrationBatchCreator.go @@ -0,0 +1,130 @@ +package ethereum + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-chain-core-go/core/check" +) + +var zero = big.NewInt(0) + +// ArgsMigrationBatchCreator is the argument for the NewMigrationBatchCreator constructor +type ArgsMigrationBatchCreator struct { + TokensList []string + TokensMapper TokensMapper + Erc20ContractsHolder Erc20ContractsHolder + SafeContractAddress common.Address + SafeContractWrapper SafeContractWrapper +} + +type migrationBatchCreator struct { + tokensList []string + tokensMapper TokensMapper + erc20ContractsHolder Erc20ContractsHolder + safeContractAddress common.Address + safeContractWrapper SafeContractWrapper +} + +// NewMigrationBatchCreator creates a new instance of type migrationBatchCreator that is able to generate the migration batch output file +func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCreator, error) { + if len(args.TokensList) == 0 { + return nil, errEmptyTokensList + } + if check.IfNil(args.TokensMapper) { + return nil, errNilTokensMapper + } + if check.IfNil(args.Erc20ContractsHolder) { + return nil, errNilErc20ContractsHolder + } + + return &migrationBatchCreator{ + tokensList: args.TokensList, + tokensMapper: args.TokensMapper, + erc20ContractsHolder: args.Erc20ContractsHolder, + safeContractAddress: args.SafeContractAddress, + }, nil +} + +// CreateBatchInfo creates an instance of type BatchInfo +func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context) (*BatchInfo, error) { + batchesCount, err := creator.safeContractWrapper.BatchesCount(&bind.CallOpts{Context: ctx}) + if err != nil { + return nil, err + } + + depositsCount, err := creator.safeContractWrapper.DepositsCount(&bind.CallOpts{Context: ctx}) + if err != nil { + return nil, err + } + + deposits, err := creator.fetchERC20ContractsAddresses(ctx, depositsCount) + if err != nil { + return nil, err + } + + err = creator.fetchBalances(ctx, deposits) + if err != nil { + return nil, err + } + + return creator.assembleBatchInfo(batchesCount, deposits) +} + +func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.Context, lastDepositNonce uint64) ([]*DepositInfo, error) { + deposits := make([]*DepositInfo, 0, len(creator.tokensList)) + for idx, token := range creator.tokensList { + addressBytes, err := creator.tokensMapper.ConvertToken(ctx, []byte(token)) + if err != nil { + return nil, err + } + + deposit := &DepositInfo{ + DepositNonce: lastDepositNonce + uint64(1+idx), + Token: token, + ContractAddress: common.BytesToAddress(addressBytes).String(), + contractAddress: common.BytesToAddress(addressBytes), + Amount: "", + } + + deposits = append(deposits, deposit) + } + + return deposits, nil +} + +func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposits []*DepositInfo) error { + for _, deposit := range deposits { + balance, err := creator.erc20ContractsHolder.BalanceOf(ctx, deposit.contractAddress, creator.safeContractAddress) + if err != nil { + return fmt.Errorf("%w for address %s in ERC20 contract %s", err, creator.safeContractAddress.String(), deposit.contractAddress.String()) + } + + deposit.amount = balance + deposit.Amount = balance.String() + } + + return nil +} + +func (creator *migrationBatchCreator) assembleBatchInfo(batchesCount uint64, deposits []*DepositInfo) (*BatchInfo, error) { + batchInfo := &BatchInfo{ + OldSafeContractAddress: creator.safeContractAddress.String(), + NewSafeContractAddress: "", + BatchID: batchesCount + 1, + DepositsInfo: make([]*DepositInfo, 0, len(deposits)), + } + + for _, deposit := range deposits { + if deposit.amount.Cmp(zero) <= 0 { + continue + } + + batchInfo.DepositsInfo = append(batchInfo.DepositsInfo, deposit) + } + + return batchInfo, nil +} From 09c9833422bf4f7496d0006e7db82cbfbe0bf34a Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 2 Sep 2024 16:12:45 +0300 Subject: [PATCH 02/15] - fixes - added unit tests --- cmd/migration/config/config.toml | 2 +- cmd/migration/config/migration.json | 82 +++++- cmd/migration/flags.go | 13 +- cmd/migration/main.go | 38 ++- config/tomlConfigs_test.go | 6 +- executors/ethereum/errors.go | 1 + executors/ethereum/migrationBatchCreator.go | 12 +- .../ethereum/migrationBatchCreator_test.go | 236 ++++++++++++++++++ testsCommon/bridge/safeContractWrapperStub.go | 27 ++ 9 files changed, 385 insertions(+), 32 deletions(-) create mode 100644 executors/ethereum/migrationBatchCreator_test.go create mode 100644 testsCommon/bridge/safeContractWrapperStub.go diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml index 77cfefc0..e6a8e24f 100644 --- a/cmd/migration/config/config.toml +++ b/cmd/migration/config/config.toml @@ -19,7 +19,7 @@ [MultiversX] NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address - SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" [MultiversX.Proxy] CacherExpirationSeconds = 600 # the caching time in seconds diff --git a/cmd/migration/config/migration.json b/cmd/migration/config/migration.json index 4698c37e..3f03dc36 100644 --- a/cmd/migration/config/migration.json +++ b/cmd/migration/config/migration.json @@ -1,19 +1,79 @@ { - "OldSafeContractAddress": "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", - "NewSafeContractAddress": "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", - "BatchID": 22222, + "OldSafeContractAddress": "0x92A26975433A61CF1134802586aa669bAB8B69f3", + "NewSafeContractAddress": "0x37aaaf95887624a363effB7762D489E3C05c2a02", + "BatchID": 3547, "DepositsInfo": [ { - "DepositNonce": 1, - "Token": "USDC", - "ContractAddress": "ssss", - "Amount": "11111111111" + "DepositNonce": 4651, + "Token": "ETHUSDC-220753", + "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "Amount": "7245293620507" }, { - "DepositNonce": 2, - "Token": "MEME", - "ContractAddress": "ssss", - "Amount": "2222222" + "DepositNonce": 4652, + "Token": "ETHUTK-8cdf7a", + "ContractAddress": "0xdc9Ac3C20D1ed0B540dF9b1feDC10039Df13F99c", + "Amount": "224564169411881824066539333" + }, + { + "DepositNonce": 4653, + "Token": "ETHUSDT-9c73c6", + "ContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "Amount": "305146464594" + }, + { + "DepositNonce": 4654, + "Token": "ETHBUSD-450923", + "ContractAddress": "0x4Fabb145d64652a948d72533023f6E7A623C7C53", + "Amount": "22294352736330153151984" + }, + { + "DepositNonce": 4655, + "Token": "ETHHMT-18538a", + "ContractAddress": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", + "Amount": "435000000000000000000" + }, + { + "DepositNonce": 4656, + "Token": "ETHCGG-ee4e0c", + "ContractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", + "Amount": "1500138370967581082219795" + }, + { + "DepositNonce": 4657, + "Token": "ETHINFRA-60a3bf", + "ContractAddress": "0x013062189dc3dcc99e9Cee714c513033b8d99e3c", + "Amount": "141172595980399500424091" + }, + { + "DepositNonce": 4658, + "Token": "ETHWBTC-74e282", + "ContractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "Amount": "3898848955" + }, + { + "DepositNonce": 4659, + "Token": "ETHWETH-e1c126", + "ContractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "Amount": "725921417141355037005" + }, + { + "DepositNonce": 4660, + "Token": "ETHWSDAI-572803", + "ContractAddress": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "Amount": "5431516086574385345409" + }, + { + "DepositNonce": 4661, + "Token": "ETHWDAI-bd65f9", + "ContractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "Amount": "127608014625114134146189" + }, + { + "DepositNonce": 4662, + "Token": "ETHUMB-291202", + "ContractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", + "Amount": "4669367702477323508554075" } ] } \ No newline at end of file diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go index 2afce5a0..ee96b554 100644 --- a/cmd/migration/flags.go +++ b/cmd/migration/flags.go @@ -15,7 +15,6 @@ var ( " log level.", Value: "*:" + logger.LogDebug.String(), } - // configurationFile defines a flag for the path to the main toml configuration file configurationFile = cli.StringFlag{ Name: "config", Usage: "The `" + filePathPlaceholder + "` for the main configuration file. This TOML file contain the main " + @@ -27,6 +26,16 @@ var ( Usage: "This flag specifies the operation mode. Usage: generate, sign or execute", Value: generateMode, } + migrationJsonFile = cli.StringFlag{ + Name: "migration-file", + Usage: "The input or output .json file containing the migration data", + Value: "config/migration.json", + } + newSafeAddress = cli.StringFlag{ + Name: "new-safe-address", + Usage: "The new safe address on Ethereum", + Value: "", + } ) func getFlags() []cli.Flag { @@ -34,6 +43,8 @@ func getFlags() []cli.Flag { logLevel, configurationFile, mode, + migrationJsonFile, + newSafeAddress, } } func getFlagsConfig(ctx *cli.Context) config.ContextFlagsConfig { diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 9efb7687..273452e0 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "context" "encoding/json" "fmt" @@ -11,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" "github.com/multiversx/mx-bridge-eth-go/clients/multiversx/mappers" "github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled" @@ -74,7 +76,7 @@ func execute(ctx *cli.Context) error { operationMode := strings.ToLower(ctx.GlobalString(mode.Name)) switch operationMode { case generateMode: - return generate(cfg) + return generate(ctx, cfg) case signMode: //TODO: implement case executeMode: @@ -84,7 +86,7 @@ func execute(ctx *cli.Context) error { return fmt.Errorf("unknown execution mode: %s", operationMode) } -func generate(cfg config.MigrationToolConfig) error { +func generate(ctx *cli.Context, cfg config.MigrationToolConfig) error { argsProxy := blockchain.ArgsProxy{ ProxyURL: cfg.MultiversX.NetworkAddress, SameScState: false, @@ -99,16 +101,16 @@ func generate(cfg config.MigrationToolConfig) error { return err } - emptyAddress := data.NewAddressFromBytes(make([]byte, 0)) - safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress) + dummyAddress := data.NewAddressFromBytes(bytes.Repeat([]byte{0x1}, 32)) + multisigAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.MultisigContractAddress) if err != nil { return err } argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ - MultisigContractAddress: emptyAddress, - SafeContractAddress: safeAddress, - RelayerAddress: emptyAddress, + MultisigContractAddress: multisigAddress, + SafeContractAddress: dummyAddress, + RelayerAddress: dummyAddress, Proxy: proxy, Log: log, } @@ -136,12 +138,18 @@ func generate(cfg config.MigrationToolConfig) error { return err } + safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress) + safeInstance, err := contract.NewERC20Safe(safeEthAddress, ethClient) + if err != nil { + return err + } + argsCreator := ethereum.ArgsMigrationBatchCreator{ TokensList: cfg.WhitelistedTokens.List, TokensMapper: tokensWrapper, Erc20ContractsHolder: erc20ContractsHolder, - SafeContractAddress: common.Address{}, - SafeContractWrapper: nil, + SafeContractAddress: safeEthAddress, + SafeContractWrapper: safeInstance, } creator, err := ethereum.NewMigrationBatchCreator(argsCreator) @@ -149,12 +157,17 @@ func generate(cfg config.MigrationToolConfig) error { return err } - batchInfo, err := creator.CreateBatchInfo(context.Background()) + newSafeAddressString := ctx.GlobalString(newSafeAddress.Name) + if len(newSafeAddressString) == 0 { + return fmt.Errorf("invalid new safe address for Ethereum") + } + newSafeAddressValue := common.HexToAddress(ctx.GlobalString(newSafeAddress.Name)) + + batchInfo, err := creator.CreateBatchInfo(context.Background(), newSafeAddressValue) if err != nil { return err } - //TODO: save in a file val, err := json.MarshalIndent(batchInfo, "", " ") if err != nil { return err @@ -162,7 +175,8 @@ func generate(cfg config.MigrationToolConfig) error { log.Info(string(val)) - return nil + jsonFilename := ctx.GlobalString(migrationJsonFile.Name) + return os.WriteFile(jsonFilename, val, os.ModePerm) } func loadConfig(filepath string) (config.MigrationToolConfig, error) { diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go index 30f79d3b..4e0b3498 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -481,8 +481,8 @@ func TestMigrationToolConfig(t *testing.T) { }, }, MultiversX: MultiversXConfig{ - NetworkAddress: "https://devnet-gateway.multiversx.com", - SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", + NetworkAddress: "https://devnet-gateway.multiversx.com", + MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", Proxy: ProxyConfig{ CacherExpirationSeconds: 600, RestAPIEntityType: "observer", @@ -534,7 +534,7 @@ func TestMigrationToolConfig(t *testing.T) { [MultiversX] NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address - SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" [MultiversX.Proxy] CacherExpirationSeconds = 600 # the caching time in seconds diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index 3bb07bac..25687f62 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -6,4 +6,5 @@ var ( errEmptyTokensList = errors.New("empty tokens list") errNilTokensMapper = errors.New("nil MultiversX to Ethereum tokens mapper") errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") + errNilSafeContractWrapper = errors.New("nil safe contract wrapper") ) diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index c208c799..bf2be86f 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -40,17 +40,21 @@ func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCr if check.IfNil(args.Erc20ContractsHolder) { return nil, errNilErc20ContractsHolder } + if check.IfNilReflect(args.SafeContractWrapper) { + return nil, errNilSafeContractWrapper + } return &migrationBatchCreator{ tokensList: args.TokensList, tokensMapper: args.TokensMapper, erc20ContractsHolder: args.Erc20ContractsHolder, safeContractAddress: args.SafeContractAddress, + safeContractWrapper: args.SafeContractWrapper, }, nil } // CreateBatchInfo creates an instance of type BatchInfo -func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context) (*BatchInfo, error) { +func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSafeAddress common.Address) (*BatchInfo, error) { batchesCount, err := creator.safeContractWrapper.BatchesCount(&bind.CallOpts{Context: ctx}) if err != nil { return nil, err @@ -71,7 +75,7 @@ func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context) (*Bat return nil, err } - return creator.assembleBatchInfo(batchesCount, deposits) + return creator.assembleBatchInfo(batchesCount, deposits, newSafeAddress) } func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.Context, lastDepositNonce uint64) ([]*DepositInfo, error) { @@ -110,10 +114,10 @@ func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposit return nil } -func (creator *migrationBatchCreator) assembleBatchInfo(batchesCount uint64, deposits []*DepositInfo) (*BatchInfo, error) { +func (creator *migrationBatchCreator) assembleBatchInfo(batchesCount uint64, deposits []*DepositInfo, newSafeAddress common.Address) (*BatchInfo, error) { batchInfo := &BatchInfo{ OldSafeContractAddress: creator.safeContractAddress.String(), - NewSafeContractAddress: "", + NewSafeContractAddress: newSafeAddress.String(), BatchID: batchesCount + 1, DepositsInfo: make([]*DepositInfo, 0, len(deposits)), } diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go new file mode 100644 index 00000000..b5edcba4 --- /dev/null +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -0,0 +1,236 @@ +package ethereum + +import ( + "bytes" + "context" + "errors" + "fmt" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + "github.com/stretchr/testify/assert" +) + +var safeContractAddress = common.HexToAddress(strings.Repeat("9", 40)) +var tkn1Erc20Address = bytes.Repeat([]byte("2"), 20) +var tkn2Erc20Address = bytes.Repeat([]byte("3"), 20) +var balanceOfTkn1 = big.NewInt(37) +var balanceOfTkn2 = big.NewInt(38) + +func createMockArgsForMigrationBatchCreator() ArgsMigrationBatchCreator { + return ArgsMigrationBatchCreator{ + TokensList: []string{"tkn1", "tkn2"}, + TokensMapper: &bridge.TokensMapperStub{}, + Erc20ContractsHolder: &bridge.ERC20ContractsHolderStub{}, + SafeContractAddress: safeContractAddress, + SafeContractWrapper: &bridge.SafeContractWrapperStub{}, + } +} + +func TestNewMigrationBatchCreator(t *testing.T) { + t.Parallel() + + t.Run("nil or empty tokens list should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.TokensList = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errEmptyTokensList, err) + + args = createMockArgsForMigrationBatchCreator() + args.TokensList = make([]string, 0) + + creator, err = NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errEmptyTokensList, err) + }) + t.Run("nil tokens mapper should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.TokensMapper = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilTokensMapper, err) + }) + t.Run("nil erc20 contracts holder should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.Erc20ContractsHolder = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilErc20ContractsHolder, err) + }) + t.Run("nil safe contract wrapper should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.SafeContractWrapper = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilSafeContractWrapper, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + + creator, err := NewMigrationBatchCreator(args) + assert.NotNil(t, creator) + assert.Nil(t, err) + }) +} + +func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + newSafeContractAddress := common.HexToAddress(strings.Repeat("8", 40)) + t.Run("BatchesCount errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.SafeContractWrapper = &bridge.SafeContractWrapperStub{ + BatchesCountCalled: func(opts *bind.CallOpts) (uint64, error) { + return 0, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("DepositsCount errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.SafeContractWrapper = &bridge.SafeContractWrapperStub{ + DepositsCountCalled: func(opts *bind.CallOpts) (uint64, error) { + return 0, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("ConvertToken errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.TokensMapper = &bridge.TokensMapperStub{ + ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { + return nil, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("BalanceOf errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.Erc20ContractsHolder = &bridge.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.ErrorIs(t, err, expectedErr) + assert.Nil(t, batch) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + depositCountStart := uint64(39) + depositCount := depositCountStart + batchesCount := uint64(2244) + args := createMockArgsForMigrationBatchCreator() + args.TokensMapper = &bridge.TokensMapperStub{ + ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { + if string(sourceBytes) == "tkn1" { + return tkn1Erc20Address, nil + } + if string(sourceBytes) == "tkn2" { + return tkn2Erc20Address, nil + } + + return nil, fmt.Errorf("unexpected source bytes") + }, + } + args.Erc20ContractsHolder = &bridge.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + assert.Equal(t, address.String(), safeContractAddress.String()) + + if string(erc20Address.Bytes()) == string(tkn1Erc20Address) { + return balanceOfTkn1, nil + } + if string(erc20Address.Bytes()) == string(tkn2Erc20Address) { + return balanceOfTkn2, nil + } + + return nil, fmt.Errorf("unexpected ERC20 contract address") + }, + } + args.SafeContractWrapper = &bridge.SafeContractWrapperStub{ + DepositsCountCalled: func(opts *bind.CallOpts) (uint64, error) { + depositCountValue := depositCount + depositCount++ + + return depositCountValue, nil + + }, + BatchesCountCalled: func(opts *bind.CallOpts) (uint64, error) { + return batchesCount, nil + }, + } + creator, _ := NewMigrationBatchCreator(args) + + expectedBatch := &BatchInfo{ + OldSafeContractAddress: safeContractAddress.String(), + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: 2245, + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 40, + Token: "tkn1", + ContractAddress: common.BytesToAddress(tkn1Erc20Address).String(), + contractAddress: common.BytesToAddress(tkn1Erc20Address), + amount: big.NewInt(37), + Amount: "37", + }, + { + DepositNonce: 41, + Token: "tkn2", + ContractAddress: common.BytesToAddress(tkn2Erc20Address).String(), + contractAddress: common.BytesToAddress(tkn2Erc20Address), + amount: big.NewInt(38), + Amount: "38", + }, + }, + } + + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.Nil(t, err) + assert.Equal(t, expectedBatch, batch) + }) + +} diff --git a/testsCommon/bridge/safeContractWrapperStub.go b/testsCommon/bridge/safeContractWrapperStub.go new file mode 100644 index 00000000..9da42c08 --- /dev/null +++ b/testsCommon/bridge/safeContractWrapperStub.go @@ -0,0 +1,27 @@ +package bridge + +import "github.com/ethereum/go-ethereum/accounts/abi/bind" + +// SafeContractWrapperStub - +type SafeContractWrapperStub struct { + DepositsCountCalled func(opts *bind.CallOpts) (uint64, error) + BatchesCountCalled func(opts *bind.CallOpts) (uint64, error) +} + +// DepositsCount - +func (stub *SafeContractWrapperStub) DepositsCount(opts *bind.CallOpts) (uint64, error) { + if stub.DepositsCountCalled != nil { + return stub.DepositsCountCalled(opts) + } + + return 0, nil +} + +// BatchesCount - +func (stub *SafeContractWrapperStub) BatchesCount(opts *bind.CallOpts) (uint64, error) { + if stub.BatchesCountCalled != nil { + return stub.BatchesCountCalled(opts) + } + + return 0, nil +} From 0b73e3305ec7802e74456196d87dbf5c7e11e919 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Tue, 3 Sep 2024 14:06:04 +0300 Subject: [PATCH 03/15] - added signing capability to the migration tool --- clients/errors.go | 3 + clients/ethereum/client.go | 40 +++---- clients/ethereum/client_test.go | 89 +++++++++++---- clients/ethereum/cryptoHandler.go | 63 +++++++++++ clients/ethereum/cryptoHandler_test.go | 102 ++++++++++++++++++ clients/ethereum/interface.go | 8 ++ clients/ethereum/testdata/nok-ethereum-key | 1 + clients/ethereum/testdata/ok-ethereum-key | 1 + cmd/migration/flags.go | 8 +- cmd/migration/main.go | 43 ++++++-- executors/ethereum/common.go | 8 ++ executors/ethereum/errors.go | 1 + executors/ethereum/migrationBatchCreator.go | 30 ++++++ .../ethereum/migrationBatchCreator_test.go | 1 + factory/errors.go | 1 - factory/ethMultiversXBridgeComponents.go | 19 +--- testsCommon/bridge/cryptoHandlerStub.go | 48 +++++++++ 17 files changed, 393 insertions(+), 73 deletions(-) create mode 100644 clients/ethereum/cryptoHandler.go create mode 100644 clients/ethereum/cryptoHandler_test.go create mode 100644 clients/ethereum/testdata/nok-ethereum-key create mode 100644 clients/ethereum/testdata/ok-ethereum-key create mode 100644 testsCommon/bridge/cryptoHandlerStub.go diff --git a/clients/errors.go b/clients/errors.go index d095dcec..021d3515 100644 --- a/clients/errors.go +++ b/clients/errors.go @@ -35,4 +35,7 @@ var ( // ErrNoPendingBatchAvailable signals that no pending batch is available ErrNoPendingBatchAvailable = errors.New("no pending batch available") + + // ErrNilCryptoHandler signals that a nil crypto handler key was provided + ErrNilCryptoHandler = errors.New("nil crypto handler") ) diff --git a/clients/ethereum/client.go b/clients/ethereum/client.go index dfea49d7..2268b42d 100644 --- a/clients/ethereum/client.go +++ b/clients/ethereum/client.go @@ -2,14 +2,12 @@ package ethereum import ( "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/clients" @@ -34,7 +32,7 @@ type ArgsEthereumClient struct { Log chainCore.Logger AddressConverter core.AddressConverter Broadcaster Broadcaster - PrivateKey *ecdsa.PrivateKey + CryptoHandler CryptoHandler TokensMapper TokensMapper SignatureHolder SignaturesHolder SafeContractAddress common.Address @@ -52,8 +50,7 @@ type client struct { log chainCore.Logger addressConverter core.AddressConverter broadcaster Broadcaster - privateKey *ecdsa.PrivateKey - publicKey *ecdsa.PublicKey + cryptoHandler CryptoHandler tokensMapper TokensMapper signatureHolder SignaturesHolder safeContractAddress common.Address @@ -76,20 +73,13 @@ func NewEthereumClient(args ArgsEthereumClient) (*client, error) { return nil, err } - publicKey := args.PrivateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return nil, errPublicKeyCast - } - c := &client{ clientWrapper: args.ClientWrapper, erc20ContractsHandler: args.Erc20ContractsHandler, log: args.Log, addressConverter: args.AddressConverter, broadcaster: args.Broadcaster, - privateKey: args.PrivateKey, - publicKey: publicKeyECDSA, + cryptoHandler: args.CryptoHandler, tokensMapper: args.TokensMapper, signatureHolder: args.SignatureHolder, safeContractAddress: args.SafeContractAddress, @@ -102,7 +92,7 @@ func NewEthereumClient(args ArgsEthereumClient) (*client, error) { } c.log.Info("NewEthereumClient", - "relayer address", crypto.PubkeyToAddress(*publicKeyECDSA), + "relayer address", c.cryptoHandler.GetAddress(), "safe contract address", c.safeContractAddress.String()) return c, err @@ -124,8 +114,8 @@ func checkArgs(args ArgsEthereumClient) error { if check.IfNil(args.Broadcaster) { return errNilBroadcaster } - if args.PrivateKey == nil { - return clients.ErrNilPrivateKey + if check.IfNil(args.CryptoHandler) { + return clients.ErrNilCryptoHandler } if check.IfNil(args.TokensMapper) { return clients.ErrNilTokensMapper @@ -256,7 +246,7 @@ func (c *client) WasExecuted(ctx context.Context, batchID uint64) (bool, error) // BroadcastSignatureForMessageHash will send the signature for the provided message hash func (c *client) BroadcastSignatureForMessageHash(msgHash common.Hash) { - signature, err := crypto.Sign(msgHash.Bytes(), c.privateKey) + signature, err := c.cryptoHandler.Sign(msgHash) if err != nil { c.log.Error("error generating signature", "msh hash", msgHash, "error", err) return @@ -267,6 +257,11 @@ func (c *client) BroadcastSignatureForMessageHash(msgHash common.Hash) { // GenerateMessageHash will generate the message hash based on the provided batch func (c *client) GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchId uint64) (common.Hash, error) { + return GenerateMessageHash(batch, batchId) +} + +// GenerateMessageHash will generate the message hash based on the provided batch +func GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchId uint64) (common.Hash, error) { if batch == nil { return common.Hash{}, clients.ErrNilBatch } @@ -336,9 +331,7 @@ func (c *client) ExecuteTransfer( return "", fmt.Errorf("%w in client.ExecuteTransfer", clients.ErrMultisigContractPaused) } - fromAddress := crypto.PubkeyToAddress(*c.publicKey) - - nonce, err := c.getNonce(ctx, fromAddress) + nonce, err := c.getNonce(ctx, c.cryptoHandler.GetAddress()) if err != nil { return "", err } @@ -348,7 +341,7 @@ func (c *client) ExecuteTransfer( return "", err } - auth, err := bind.NewKeyedTransactorWithChainID(c.privateKey, chainId) + auth, err := c.cryptoHandler.CreateKeyedTransactor(chainId) if err != nil { return "", err } @@ -496,10 +489,7 @@ func (c *client) WhitelistedTokens(ctx context.Context, token common.Address) (b } func (c *client) checkRelayerFundsForFee(ctx context.Context, transferFee *big.Int) error { - - ethereumRelayerAddress := crypto.PubkeyToAddress(*c.publicKey) - - existingBalance, err := c.clientWrapper.BalanceAt(ctx, ethereumRelayerAddress, nil) + existingBalance, err := c.clientWrapper.BalanceAt(ctx, c.cryptoHandler.GetAddress(), nil) if err != nil { return err } diff --git a/clients/ethereum/client_test.go b/clients/ethereum/client_test.go index fca14a04..48ea7fce 100644 --- a/clients/ethereum/client_test.go +++ b/clients/ethereum/client_test.go @@ -13,7 +13,6 @@ import ( "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/crypto" "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" @@ -33,8 +32,6 @@ var expectedRecipients = []common.Address{common.BytesToAddress([]byte("to1")), var expectedNonces = []*big.Int{big.NewInt(10), big.NewInt(30)} func createMockEthereumClientArgs() ArgsEthereumClient { - sk, _ := crypto.HexToECDSA("9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1") - addressConverter, err := converters.NewAddressConverter() if err != nil { panic(err) @@ -46,7 +43,7 @@ func createMockEthereumClientArgs() ArgsEthereumClient { Log: logger.GetOrCreate("test"), AddressConverter: addressConverter, Broadcaster: &testsCommon.BroadcasterStub{}, - PrivateKey: sk, + CryptoHandler: &bridgeTests.CryptoHandlerStub{}, TokensMapper: &bridgeTests.TokensMapperStub{ ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { return append([]byte("ERC20"), sourceBytes...), nil @@ -137,12 +134,12 @@ func TestNewEthereumClient(t *testing.T) { assert.Equal(t, errNilBroadcaster, err) assert.True(t, check.IfNil(c)) }) - t.Run("nil private key", func(t *testing.T) { + t.Run("nil crypto handler", func(t *testing.T) { args := createMockEthereumClientArgs() - args.PrivateKey = nil + args.CryptoHandler = nil c, err := NewEthereumClient(args) - assert.Equal(t, clients.ErrNilPrivateKey, err) + assert.Equal(t, clients.ErrNilCryptoHandler, err) assert.True(t, check.IfNil(c)) }) t.Run("nil tokens mapper", func(t *testing.T) { @@ -475,23 +472,54 @@ func TestClient_GenerateMessageHash(t *testing.T) { func TestClient_BroadcastSignatureForMessageHash(t *testing.T) { t.Parallel() - expectedSig := "b556014dd984183e4662dc3204e522a5a92093fd6f64bb2da9c1b66b8d5ad12d774e05728b83c76bf09bb91af93ede4118f59aa949c7d02c86051dd0fa140c9900" - broadcastCalled := false + t.Run("sign failed should not broadcast", func(t *testing.T) { + t.Parallel() - hash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b08") - args := createMockEthereumClientArgs() - args.Broadcaster = &testsCommon.BroadcasterStub{ - BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { - assert.Equal(t, hash.Bytes(), messageHash) - assert.Equal(t, expectedSig, hex.EncodeToString(signature)) - broadcastCalled = true - }, - } + expectedError := errors.New("expected error") + hash := common.HexToHash("hash") + args := createMockEthereumClientArgs() + args.Broadcaster = &testsCommon.BroadcasterStub{ + BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { + assert.Fail(t, "should have not called bradcast") + }, + } + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + SignCalled: func(msgHash common.Hash) ([]byte, error) { + assert.Equal(t, msgHash.Bytes(), hash.Bytes()) + return nil, expectedError + }, + } - c, _ := NewEthereumClient(args) - c.BroadcastSignatureForMessageHash(hash) + c, _ := NewEthereumClient(args) + c.BroadcastSignatureForMessageHash(hash) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + expectedSig := "expected sig" + broadcastCalled := false + + hash := common.HexToHash("hash") + args := createMockEthereumClientArgs() + args.Broadcaster = &testsCommon.BroadcasterStub{ + BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { + assert.Equal(t, hash.Bytes(), messageHash) + assert.Equal(t, expectedSig, string(signature)) + broadcastCalled = true + }, + } + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + SignCalled: func(msgHash common.Hash) ([]byte, error) { + assert.Equal(t, msgHash.Bytes(), hash.Bytes()) + return []byte(expectedSig), nil + }, + } - assert.True(t, broadcastCalled) + c, _ := NewEthereumClient(args) + c.BroadcastSignatureForMessageHash(hash) + + assert.True(t, broadcastCalled) + }) } func TestClient_WasExecuted(t *testing.T) { @@ -517,6 +545,11 @@ func TestClient_ExecuteTransfer(t *testing.T) { t.Parallel() args := createMockEthereumClientArgs() + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } batch := createMockTransferBatch() argLists := batchProcessor.ExtractListMvxToEth(batch) signatures := make([][]byte, 10) @@ -589,6 +622,18 @@ func TestClient_ExecuteTransfer(t *testing.T) { assert.Equal(t, "", hash) assert.True(t, errors.Is(err, expectedErr)) }) + t.Run("create keyed transactor fails", func(t *testing.T) { + expectedErr := errors.New("expected error create keyed transactor") + c, _ := NewEthereumClient(args) + c.cryptoHandler = &bridgeTests.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return nil, expectedErr + }, + } + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) + assert.Equal(t, "", hash) + assert.True(t, errors.Is(err, expectedErr)) + }) t.Run("get current gas price fails", func(t *testing.T) { expectedErr := errors.New("expected error get current gas price") c, _ := NewEthereumClient(args) @@ -599,7 +644,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { } hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) - assert.True(t, errors.Is(err, expectedErr)) + assert.ErrorIs(t, err, expectedErr) }) t.Run("not enough quorum", func(t *testing.T) { c, _ := NewEthereumClient(args) diff --git a/clients/ethereum/cryptoHandler.go b/clients/ethereum/cryptoHandler.go new file mode 100644 index 00000000..0cc05199 --- /dev/null +++ b/clients/ethereum/cryptoHandler.go @@ -0,0 +1,63 @@ +package ethereum + +import ( + "crypto/ecdsa" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-bridge-eth-go/core/converters" +) + +type cryptoHandler struct { + privateKey *ecdsa.PrivateKey + publicKey *ecdsa.PublicKey + address common.Address +} + +// NewCryptoHandler creates a new instance of type cryptoHandler able to sign messages and provide the containing public key +func NewCryptoHandler(privateKeyFilename string) (*cryptoHandler, error) { + privateKeyBytes, err := os.ReadFile(privateKeyFilename) + if err != nil { + return nil, err + } + privateKeyString := converters.TrimWhiteSpaceCharacters(string(privateKeyBytes)) + privateKey, err := ethCrypto.HexToECDSA(privateKeyString) + if err != nil { + return nil, err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errPublicKeyCast + } + + return &cryptoHandler{ + privateKey: privateKey, + publicKey: publicKeyECDSA, + address: ethCrypto.PubkeyToAddress(*publicKeyECDSA), + }, nil +} + +// Sign signs the provided message hash with the containing private key +func (handler *cryptoHandler) Sign(msgHash common.Hash) ([]byte, error) { + return ethCrypto.Sign(msgHash.Bytes(), handler.privateKey) +} + +// GetAddress returns the corresponding address of the containing public key +func (handler *cryptoHandler) GetAddress() common.Address { + return handler.address +} + +// CreateKeyedTransactor creates a keyed transactor used to create transactions on Ethereum chain +func (handler *cryptoHandler) CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) { + return bind.NewKeyedTransactorWithChainID(handler.privateKey, chainId) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (handler *cryptoHandler) IsInterfaceNil() bool { + return handler == nil +} diff --git a/clients/ethereum/cryptoHandler_test.go b/clients/ethereum/cryptoHandler_test.go new file mode 100644 index 00000000..79d8c99f --- /dev/null +++ b/clients/ethereum/cryptoHandler_test.go @@ -0,0 +1,102 @@ +package ethereum + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +func TestNewCryptoHandler(t *testing.T) { + t.Parallel() + + t.Run("invalid file should error", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("missing file") + assert.Nil(t, handler) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "open missing file: no such file or directory") + }) + t.Run("invalid private key file", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("./testdata/nok-ethereum-key") + assert.Nil(t, handler) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid hex data for private key") + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("./testdata/ok-ethereum-key") + assert.NotNil(t, handler) + assert.Nil(t, err) + }) +} + +func TestCryptoHandler_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *cryptoHandler + assert.True(t, instance.IsInterfaceNil()) + + instance = &cryptoHandler{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestCryptoHandler_Sign(t *testing.T) { + t.Parallel() + + t.Run("test 1", func(t *testing.T) { + expectedSig := "b556014dd984183e4662dc3204e522a5a92093fd6f64bb2da9c1b66b8d5ad12d774e05728b83c76bf09bb91af93ede4118f59aa949c7d02c86051dd0fa140c9900" + msgHash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b08") + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + sig, err := handler.Sign(msgHash) + assert.Nil(t, err) + assert.Equal(t, expectedSig, hex.EncodeToString(sig)) + }) + t.Run("test 2", func(t *testing.T) { + expectedSig := "9abff5ecad356a82855f3ecc816cad5d19315ab812f1affeed7f8020accf01127d4c41ed56ff1b3053b64957a19aa1c6fd7dd1b5aa53065b0df231f517bfe89f01" + msgHash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b09") + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + sig, err := handler.Sign(msgHash) + assert.Nil(t, err) + assert.Equal(t, expectedSig, hex.EncodeToString(sig)) + }) +} + +func TestCryptoHandler_GetAddress(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + expectedAddress := common.HexToAddress("0x3FE464Ac5aa562F7948322F92020F2b668D543d8") + + assert.Equal(t, expectedAddress, handler.GetAddress()) +} + +func TestCryptoHandler_CreateKeyedTransactor(t *testing.T) { + t.Parallel() + + t.Run("nil chain ID should error", func(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + opts, err := handler.CreateKeyedTransactor(nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "no chain id specified") + assert.Nil(t, opts) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + opts, err := handler.CreateKeyedTransactor(big.NewInt(1)) + assert.Nil(t, err) + assert.NotNil(t, opts) + }) +} diff --git a/clients/ethereum/interface.go b/clients/ethereum/interface.go index 51d82a66..d6a2ca9d 100644 --- a/clients/ethereum/interface.go +++ b/clients/ethereum/interface.go @@ -73,3 +73,11 @@ type erc20ContractWrapper interface { BalanceOf(ctx context.Context, account common.Address) (*big.Int, error) IsInterfaceNil() bool } + +// CryptoHandler defines the operations for a component that expose some crypto primitives +type CryptoHandler interface { + Sign(msgHash common.Hash) ([]byte, error) + GetAddress() common.Address + CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) + IsInterfaceNil() bool +} diff --git a/clients/ethereum/testdata/nok-ethereum-key b/clients/ethereum/testdata/nok-ethereum-key new file mode 100644 index 00000000..3c506c9c --- /dev/null +++ b/clients/ethereum/testdata/nok-ethereum-key @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb diff --git a/clients/ethereum/testdata/ok-ethereum-key b/clients/ethereum/testdata/ok-ethereum-key new file mode 100644 index 00000000..5675c4b6 --- /dev/null +++ b/clients/ethereum/testdata/ok-ethereum-key @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1 diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go index ee96b554..37b07063 100644 --- a/cmd/migration/flags.go +++ b/cmd/migration/flags.go @@ -1,6 +1,8 @@ package main import ( + "path" + "github.com/multiversx/mx-bridge-eth-go/config" logger "github.com/multiversx/mx-chain-logger-go" "github.com/urfave/cli" @@ -23,13 +25,13 @@ var ( } mode = cli.StringFlag{ Name: "mode", - Usage: "This flag specifies the operation mode. Usage: generate, sign or execute", - Value: generateMode, + Usage: "This flag specifies the operation mode. Usage: sign or execute", + Value: signMode, } migrationJsonFile = cli.StringFlag{ Name: "migration-file", Usage: "The input or output .json file containing the migration data", - Value: "config/migration.json", + Value: path.Join(configPath, "migration.json"), } newSafeAddress = cli.StringFlag{ Name: "new-safe-address", diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 273452e0..93bcd293 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -3,9 +3,11 @@ package main import ( "bytes" "context" + "encoding/hex" "encoding/json" "fmt" "os" + "path" "strings" "time" @@ -28,9 +30,9 @@ import ( const ( filePathPlaceholder = "[path]" - generateMode = "generate" signMode = "sign" executeMode = "execute" + configPath = "config" ) var log = logger.GetOrCreate("main") @@ -75,10 +77,8 @@ func execute(ctx *cli.Context) error { operationMode := strings.ToLower(ctx.GlobalString(mode.Name)) switch operationMode { - case generateMode: - return generate(ctx, cfg) case signMode: - //TODO: implement + return generateAndSign(ctx, cfg) case executeMode: //TODO: implement } @@ -86,7 +86,7 @@ func execute(ctx *cli.Context) error { return fmt.Errorf("unknown execution mode: %s", operationMode) } -func generate(ctx *cli.Context, cfg config.MigrationToolConfig) error { +func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { argsProxy := blockchain.ArgsProxy{ ProxyURL: cfg.MultiversX.NetworkAddress, SameScState: false, @@ -173,10 +173,41 @@ func generate(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } + cryptoHandler, err := ethereumClient.NewCryptoHandler(cfg.Eth.PrivateKeyFile) + if err != nil { + return err + } + + signature, err := cryptoHandler.Sign(batchInfo.MessageHash) + if err != nil { + return err + } + log.Info(string(val)) + log.Info("Batch signed", + "public key", cryptoHandler.GetAddress().String(), + "message hash", batchInfo.MessageHash.String(), + "signature", signature) jsonFilename := ctx.GlobalString(migrationJsonFile.Name) - return os.WriteFile(jsonFilename, val, os.ModePerm) + err = os.WriteFile(jsonFilename, val, os.ModePerm) + if err != nil { + return err + } + + sigInfo := ðereum.SignatureInfo{ + PublicKey: cryptoHandler.GetAddress().String(), + MessageHash: batchInfo.MessageHash.String(), + Signature: hex.EncodeToString(signature), + } + + sigFilename := path.Join(configPath, fmt.Sprintf("%s.json", sigInfo.PublicKey)) + val, err = json.MarshalIndent(sigInfo, "", " ") + if err != nil { + return err + } + + return os.WriteFile(sigFilename, val, os.ModePerm) } func loadConfig(filepath string) (config.MigrationToolConfig, error) { diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go index 71f5e146..75902890 100644 --- a/executors/ethereum/common.go +++ b/executors/ethereum/common.go @@ -21,5 +21,13 @@ type BatchInfo struct { OldSafeContractAddress string `json:"OldSafeContractAddress"` NewSafeContractAddress string `json:"NewSafeContractAddress"` BatchID uint64 `json:"BatchID"` + MessageHash common.Hash `json:"MessageHash"` DepositsInfo []*DepositInfo `json:"DepositsInfo"` } + +// SignatureInfo is the struct holding signature info +type SignatureInfo struct { + PublicKey string `json:"PublicKey"` + MessageHash string `json:"MessageHash"` + Signature string `json:"Signature"` +} diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index 25687f62..20a15ff3 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -7,4 +7,5 @@ var ( errNilTokensMapper = errors.New("nil MultiversX to Ethereum tokens mapper") errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") errNilSafeContractWrapper = errors.New("nil safe contract wrapper") + errPublicKeyCast = errors.New("error casting public key to ECDSA") ) diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index bf2be86f..3571d383 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -7,6 +7,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" "github.com/multiversx/mx-chain-core-go/core/check" ) @@ -130,5 +132,33 @@ func (creator *migrationBatchCreator) assembleBatchInfo(batchesCount uint64, dep batchInfo.DepositsInfo = append(batchInfo.DepositsInfo, deposit) } + var err error + batchInfo.MessageHash, err = creator.computeMessageHash(batchInfo) + if err != nil { + return nil, err + } + return batchInfo, nil } + +func (creator *migrationBatchCreator) computeMessageHash(batch *BatchInfo) (common.Hash, error) { + tokens := make([]common.Address, 0, len(batch.DepositsInfo)) + recipients := make([]common.Address, 0, len(batch.DepositsInfo)) + amounts := make([]*big.Int, 0, len(batch.DepositsInfo)) + nonces := make([]*big.Int, 0, len(batch.DepositsInfo)) + for _, deposit := range batch.DepositsInfo { + tokens = append(tokens, common.HexToAddress(deposit.ContractAddress)) + recipients = append(recipients, common.HexToAddress(batch.NewSafeContractAddress)) + amounts = append(amounts, deposit.amount) + nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) + } + + args := &batchProcessor.ArgListsBatch{ + EthTokens: tokens, + Recipients: recipients, + Amounts: amounts, + Nonces: nonces, + } + + return ethereum.GenerateMessageHash(args, batch.BatchID) +} diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go index b5edcba4..4dd7fd64 100644 --- a/executors/ethereum/migrationBatchCreator_test.go +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -208,6 +208,7 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { OldSafeContractAddress: safeContractAddress.String(), NewSafeContractAddress: newSafeContractAddress.String(), BatchID: 2245, + MessageHash: common.HexToHash("0x93915c0bea665553dfc85ec3cdf4b883100929f22d6cbbfc44db2f0ee71b3b56"), DepositsInfo: []*DepositInfo{ { DepositNonce: 40, diff --git a/factory/errors.go b/factory/errors.go index be86f098..ddfb0dce 100644 --- a/factory/errors.go +++ b/factory/errors.go @@ -9,7 +9,6 @@ var ( errNilStatusStorer = errors.New("nil status storer") errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") errMissingConfig = errors.New("missing config") - errPublicKeyCast = errors.New("error casting public key to ECDSA") errInvalidValue = errors.New("invalid value") errNilMetricsHolder = errors.New("nil metrics holder") errNilStatusHandler = errors.New("nil status handler") diff --git a/factory/ethMultiversXBridgeComponents.go b/factory/ethMultiversXBridgeComponents.go index e25e0927..8bbc5f98 100644 --- a/factory/ethMultiversXBridgeComponents.go +++ b/factory/ethMultiversXBridgeComponents.go @@ -2,15 +2,12 @@ package factory import ( "context" - "crypto/ecdsa" "fmt" "io" - "os" "sync" "time" "github.com/ethereum/go-ethereum/common" - ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/disabled" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps/ethToMultiversX" @@ -366,22 +363,12 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE return err } - privateKeyBytes, err := os.ReadFile(ethereumConfigs.PrivateKeyFile) - if err != nil { - return err - } - privateKeyString := converters.TrimWhiteSpaceCharacters(string(privateKeyBytes)) - privateKey, err := ethCrypto.HexToECDSA(privateKeyString) + cryptoHandler, err := ethereum.NewCryptoHandler(ethereumConfigs.PrivateKeyFile) if err != nil { return err } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return errPublicKeyCast - } - components.ethereumRelayerAddress = ethCrypto.PubkeyToAddress(*publicKeyECDSA) + components.ethereumRelayerAddress = cryptoHandler.GetAddress() tokensMapper, err := mappers.NewErc20ToMultiversXMapper(components.mxDataGetter) if err != nil { @@ -404,7 +391,7 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE Log: core.NewLoggerWithIdentifier(logger.GetOrCreate(ethClientLogId), ethClientLogId), AddressConverter: components.addressConverter, Broadcaster: components.broadcaster, - PrivateKey: privateKey, + CryptoHandler: cryptoHandler, TokensMapper: tokensMapper, SignatureHolder: signaturesHolder, SafeContractAddress: safeContractAddress, diff --git a/testsCommon/bridge/cryptoHandlerStub.go b/testsCommon/bridge/cryptoHandlerStub.go new file mode 100644 index 00000000..262ce7ff --- /dev/null +++ b/testsCommon/bridge/cryptoHandlerStub.go @@ -0,0 +1,48 @@ +package bridge + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" +) + +// CryptoHandlerStub - +type CryptoHandlerStub struct { + SignCalled func(msgHash common.Hash) ([]byte, error) + GetAddressCalled func() common.Address + CreateKeyedTransactorCalled func(chainId *big.Int) (*bind.TransactOpts, error) +} + +// Sign - +func (stub *CryptoHandlerStub) Sign(msgHash common.Hash) ([]byte, error) { + if stub.SignCalled != nil { + return stub.SignCalled(msgHash) + } + + return make([]byte, 0), nil +} + +// GetAddress - +func (stub *CryptoHandlerStub) GetAddress() common.Address { + if stub.GetAddressCalled != nil { + return stub.GetAddressCalled() + } + + return common.BytesToAddress(make([]byte, 0)) +} + +// CreateKeyedTransactor - +func (stub *CryptoHandlerStub) CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) { + if stub.CreateKeyedTransactorCalled != nil { + return stub.CreateKeyedTransactorCalled(chainId) + } + + return nil, fmt.Errorf("not implemented") +} + +// IsInterfaceNil - +func (stub *CryptoHandlerStub) IsInterfaceNil() bool { + return stub == nil +} From 2fe6240a24181e805b4545e9488087f9632e6251 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Tue, 3 Sep 2024 14:11:15 +0300 Subject: [PATCH 04/15] - linter fix --- executors/ethereum/errors.go | 1 - 1 file changed, 1 deletion(-) diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index 20a15ff3..25687f62 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -7,5 +7,4 @@ var ( errNilTokensMapper = errors.New("nil MultiversX to Ethereum tokens mapper") errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") errNilSafeContractWrapper = errors.New("nil safe contract wrapper") - errPublicKeyCast = errors.New("error casting public key to ECDSA") ) From 703a7785b3d9bf881637362e857bad8eba57599b Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Tue, 3 Sep 2024 17:11:29 +0300 Subject: [PATCH 05/15] - made the migration tool automatically fetch the tokens list --- clients/multiversx/mxClientDataGetter.go | 9 ++ clients/multiversx/mxClientDataGetter_test.go | 29 +++++ cmd/migration/config/config.toml | 16 --- cmd/migration/main.go | 9 +- config/config.go | 12 +- config/tomlConfigs_test.go | 16 --- executors/ethereum/errors.go | 9 +- executors/ethereum/interface.go | 7 ++ executors/ethereum/migrationBatchCreator.go | 56 +++++++--- .../ethereum/migrationBatchCreator_test.go | 103 ++++++++++++------ testsCommon/bridge/dataGetterStub.go | 10 ++ 11 files changed, 169 insertions(+), 107 deletions(-) diff --git a/clients/multiversx/mxClientDataGetter.go b/clients/multiversx/mxClientDataGetter.go index aae649f0..324f03ea 100644 --- a/clients/multiversx/mxClientDataGetter.go +++ b/clients/multiversx/mxClientDataGetter.go @@ -41,6 +41,7 @@ const ( getTotalBalances = "getTotalBalances" getMintBalances = "getMintBalances" getBurnBalances = "getBurnBalances" + getAllKnownTokens = "getAllKnownTokens" ) // ArgsMXClientDataGetter is the arguments DTO used in the NewMXClientDataGetter constructor @@ -526,6 +527,14 @@ func getStatusFromBuff(buff []byte) (byte, error) { return buff[len(buff)-1], nil } +// GetAllKnownTokens returns all registered tokens +func (dataGetter *mxClientDataGetter) GetAllKnownTokens(ctx context.Context) ([][]byte, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getAllKnownTokens) + + return dataGetter.executeQueryFromBuilder(ctx, builder) +} + // IsInterfaceNil returns true if there is no value under the interface func (dataGetter *mxClientDataGetter) IsInterfaceNil() bool { return dataGetter == nil diff --git a/clients/multiversx/mxClientDataGetter_test.go b/clients/multiversx/mxClientDataGetter_test.go index d6a9a527..d21ea26b 100644 --- a/clients/multiversx/mxClientDataGetter_test.go +++ b/clients/multiversx/mxClientDataGetter_test.go @@ -1258,6 +1258,35 @@ func TestMXClientDataGetter_GetAllStakedRelayers(t *testing.T) { assert.Equal(t, providedRelayers, result) } +func TestMXClientDataGetter_GetAllKnownTokens(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + providedTokens := [][]byte{[]byte("tkn1"), []byte("tkn2")} + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getAllKnownTokens, vmRequest.FuncName) + + assert.Nil(t, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: providedTokens, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.GetAllKnownTokens(context.Background()) + assert.Nil(t, err) + assert.Equal(t, providedTokens, result) +} + func TestMultiversXClientDataGetter_GetShardCurrentNonce(t *testing.T) { t.Parallel() diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml index e6a8e24f..c5c36513 100644 --- a/cmd/migration/config/config.toml +++ b/cmd/migration/config/config.toml @@ -32,19 +32,3 @@ [Logs] LogFileLifeSpanInSec = 86400 # 24h LogFileLifeSpanInMB = 1024 # 1GB - -[WhitelistedTokens] - List = [ - "ETHUSDC-220753", - "ETHUTK-8cdf7a", - "ETHUSDT-9c73c6", - "ETHBUSD-450923", - "ETHHMT-18538a", - "ETHCGG-ee4e0c", - "ETHINFRA-60a3bf", - "ETHWBTC-74e282", - "ETHWETH-e1c126", - "ETHWSDAI-572803", - "ETHWDAI-bd65f9", - "ETHUMB-291202", - ] diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 93bcd293..cf7c19a2 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -16,7 +16,6 @@ import ( ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" - "github.com/multiversx/mx-bridge-eth-go/clients/multiversx/mappers" "github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled" "github.com/multiversx/mx-bridge-eth-go/config" "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" @@ -119,11 +118,6 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } - tokensWrapper, err := mappers.NewMultiversXToErc20Mapper(mxDataGetter) - if err != nil { - return err - } - ethClient, err := ethclient.Dial(cfg.Eth.NetworkAddress) if err != nil { return err @@ -145,8 +139,7 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { } argsCreator := ethereum.ArgsMigrationBatchCreator{ - TokensList: cfg.WhitelistedTokens.List, - TokensMapper: tokensWrapper, + MvxDataGetter: mxDataGetter, Erc20ContractsHolder: erc20ContractsHolder, SafeContractAddress: safeEthAddress, SafeContractWrapper: safeInstance, diff --git a/config/config.go b/config/config.go index 916126db..76e85dcb 100644 --- a/config/config.go +++ b/config/config.go @@ -206,15 +206,9 @@ type ScCallsModuleConfig struct { Logs LogsConfig } -// WhitelistedTokensConfig will hold the whitelisted tokens config -type WhitelistedTokensConfig struct { - List []string -} - // MigrationToolConfig is the migration tool config struct type MigrationToolConfig struct { - Eth EthereumConfig - MultiversX MultiversXConfig - Logs LogsConfig - WhitelistedTokens WhitelistedTokensConfig + Eth EthereumConfig + MultiversX MultiversXConfig + Logs LogsConfig } diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go index 4e0b3498..03d1ce67 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -494,22 +494,6 @@ func TestMigrationToolConfig(t *testing.T) { LogFileLifeSpanInSec: 86400, LogFileLifeSpanInMB: 1024, }, - WhitelistedTokens: WhitelistedTokensConfig{ - List: []string{ - "ETHUSDC-220753", - "ETHUTK-8cdf7a", - "ETHUSDT-9c73c6", - "ETHBUSD-450923", - "ETHHMT-18538a", - "ETHCGG-ee4e0c", - "ETHINFRA-60a3bf", - "ETHWBTC-74e282", - "ETHWETH-e1c126", - "ETHWSDAI-572803", - "ETHWDAI-bd65f9", - "ETHUMB-291202", - }, - }, } testString := ` diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index 25687f62..c0c19f9c 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -3,8 +3,9 @@ package ethereum import "errors" var ( - errEmptyTokensList = errors.New("empty tokens list") - errNilTokensMapper = errors.New("nil MultiversX to Ethereum tokens mapper") - errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") - errNilSafeContractWrapper = errors.New("nil safe contract wrapper") + errEmptyTokensList = errors.New("empty tokens list") + errNilMvxDataGetter = errors.New("nil MultiversX data getter") + errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") + errNilSafeContractWrapper = errors.New("nil safe contract wrapper") + errWrongERC20AddressResponse = errors.New("wrong ERC20 address response") ) diff --git a/executors/ethereum/interface.go b/executors/ethereum/interface.go index 5ddaf025..5c3bc0f4 100644 --- a/executors/ethereum/interface.go +++ b/executors/ethereum/interface.go @@ -25,3 +25,10 @@ type SafeContractWrapper interface { DepositsCount(opts *bind.CallOpts) (uint64, error) BatchesCount(opts *bind.CallOpts) (uint64, error) } + +// MvxDataGetter defines the operations for the data getter operating on MultiversX chain +type MvxDataGetter interface { + GetAllKnownTokens(ctx context.Context) ([][]byte, error) + GetERC20AddressForTokenId(ctx context.Context, tokenId []byte) ([][]byte, error) + IsInterfaceNil() bool +} diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index 3571d383..a7c4621c 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -16,16 +16,14 @@ var zero = big.NewInt(0) // ArgsMigrationBatchCreator is the argument for the NewMigrationBatchCreator constructor type ArgsMigrationBatchCreator struct { - TokensList []string - TokensMapper TokensMapper + MvxDataGetter MvxDataGetter Erc20ContractsHolder Erc20ContractsHolder SafeContractAddress common.Address SafeContractWrapper SafeContractWrapper } type migrationBatchCreator struct { - tokensList []string - tokensMapper TokensMapper + mvxDataGetter MvxDataGetter erc20ContractsHolder Erc20ContractsHolder safeContractAddress common.Address safeContractWrapper SafeContractWrapper @@ -33,11 +31,8 @@ type migrationBatchCreator struct { // NewMigrationBatchCreator creates a new instance of type migrationBatchCreator that is able to generate the migration batch output file func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCreator, error) { - if len(args.TokensList) == 0 { - return nil, errEmptyTokensList - } - if check.IfNil(args.TokensMapper) { - return nil, errNilTokensMapper + if check.IfNil(args.MvxDataGetter) { + return nil, errNilMvxDataGetter } if check.IfNil(args.Erc20ContractsHolder) { return nil, errNilErc20ContractsHolder @@ -47,8 +42,7 @@ func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCr } return &migrationBatchCreator{ - tokensList: args.TokensList, - tokensMapper: args.TokensMapper, + mvxDataGetter: args.MvxDataGetter, erc20ContractsHolder: args.Erc20ContractsHolder, safeContractAddress: args.SafeContractAddress, safeContractWrapper: args.SafeContractWrapper, @@ -67,7 +61,12 @@ func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSa return nil, err } - deposits, err := creator.fetchERC20ContractsAddresses(ctx, depositsCount) + tokensList, err := creator.getTokensList(ctx) + if err != nil { + return nil, err + } + + deposits, err := creator.fetchERC20ContractsAddresses(ctx, tokensList, depositsCount) if err != nil { return nil, err } @@ -80,19 +79,40 @@ func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSa return creator.assembleBatchInfo(batchesCount, deposits, newSafeAddress) } -func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.Context, lastDepositNonce uint64) ([]*DepositInfo, error) { - deposits := make([]*DepositInfo, 0, len(creator.tokensList)) - for idx, token := range creator.tokensList { - addressBytes, err := creator.tokensMapper.ConvertToken(ctx, []byte(token)) +func (creator *migrationBatchCreator) getTokensList(ctx context.Context) ([]string, error) { + tokens, err := creator.mvxDataGetter.GetAllKnownTokens(ctx) + if err != nil { + return nil, err + } + if len(tokens) == 0 { + return nil, fmt.Errorf("%w when calling the getAllKnownTokens function on the safe contract", errEmptyTokensList) + } + + stringTokens := make([]string, 0, len(tokens)) + for _, token := range tokens { + stringTokens = append(stringTokens, string(token)) + } + + return stringTokens, nil +} + +func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.Context, tokensList []string, lastDepositNonce uint64) ([]*DepositInfo, error) { + deposits := make([]*DepositInfo, 0, len(tokensList)) + for idx, token := range tokensList { + response, err := creator.mvxDataGetter.GetERC20AddressForTokenId(ctx, []byte(token)) if err != nil { return nil, err } + if len(response) != 1 { + return nil, fmt.Errorf("%w when querying the safe contract for token %s", + errWrongERC20AddressResponse, token) + } deposit := &DepositInfo{ DepositNonce: lastDepositNonce + uint64(1+idx), Token: token, - ContractAddress: common.BytesToAddress(addressBytes).String(), - contractAddress: common.BytesToAddress(addressBytes), + ContractAddress: common.BytesToAddress(response[0]).String(), + contractAddress: common.BytesToAddress(response[0]), Amount: "", } diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go index 4dd7fd64..801e2465 100644 --- a/executors/ethereum/migrationBatchCreator_test.go +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -23,8 +23,17 @@ var balanceOfTkn2 = big.NewInt(38) func createMockArgsForMigrationBatchCreator() ArgsMigrationBatchCreator { return ArgsMigrationBatchCreator{ - TokensList: []string{"tkn1", "tkn2"}, - TokensMapper: &bridge.TokensMapperStub{}, + MvxDataGetter: &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { + return [][]byte{ + []byte("tkn1"), + []byte("tkn2"), + }, nil + }, + GetERC20AddressForTokenIdCalled: func(ctx context.Context, tokenId []byte) ([][]byte, error) { + return [][]byte{[]byte("erc 20 address")}, nil + }, + }, Erc20ContractsHolder: &bridge.ERC20ContractsHolderStub{}, SafeContractAddress: safeContractAddress, SafeContractWrapper: &bridge.SafeContractWrapperStub{}, @@ -34,32 +43,15 @@ func createMockArgsForMigrationBatchCreator() ArgsMigrationBatchCreator { func TestNewMigrationBatchCreator(t *testing.T) { t.Parallel() - t.Run("nil or empty tokens list should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsForMigrationBatchCreator() - args.TokensList = nil - - creator, err := NewMigrationBatchCreator(args) - assert.Nil(t, creator) - assert.Equal(t, errEmptyTokensList, err) - - args = createMockArgsForMigrationBatchCreator() - args.TokensList = make([]string, 0) - - creator, err = NewMigrationBatchCreator(args) - assert.Nil(t, creator) - assert.Equal(t, errEmptyTokensList, err) - }) - t.Run("nil tokens mapper should error", func(t *testing.T) { + t.Run("nil mvx data getter should error", func(t *testing.T) { t.Parallel() args := createMockArgsForMigrationBatchCreator() - args.TokensMapper = nil + args.MvxDataGetter = nil creator, err := NewMigrationBatchCreator(args) assert.Nil(t, creator) - assert.Equal(t, errNilTokensMapper, err) + assert.Equal(t, errNilMvxDataGetter, err) }) t.Run("nil erc20 contracts holder should error", func(t *testing.T) { t.Parallel() @@ -127,12 +119,12 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { assert.Equal(t, expectedErr, err) assert.Nil(t, batch) }) - t.Run("ConvertToken errors should error", func(t *testing.T) { + t.Run("get all known tokens errors should error", func(t *testing.T) { t.Parallel() args := createMockArgsForMigrationBatchCreator() - args.TokensMapper = &bridge.TokensMapperStub{ - ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { + args.MvxDataGetter = &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { return nil, expectedErr }, } @@ -142,6 +134,47 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { assert.Equal(t, expectedErr, err) assert.Nil(t, batch) }) + t.Run("get all known tokens returns 0 tokens should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.MvxDataGetter = &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { + return make([][]byte, 0), nil + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.ErrorIs(t, err, errEmptyTokensList) + assert.Nil(t, batch) + }) + t.Run("GetERC20AddressForTokenId errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + return nil, expectedErr + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("GetERC20AddressForTokenId returns empty list should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + return make([][]byte, 0), nil + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress) + assert.ErrorIs(t, err, errWrongERC20AddressResponse) + assert.Nil(t, batch) + }) t.Run("BalanceOf errors should error", func(t *testing.T) { t.Parallel() @@ -164,17 +197,15 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { depositCount := depositCountStart batchesCount := uint64(2244) args := createMockArgsForMigrationBatchCreator() - args.TokensMapper = &bridge.TokensMapperStub{ - ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { - if string(sourceBytes) == "tkn1" { - return tkn1Erc20Address, nil - } - if string(sourceBytes) == "tkn2" { - return tkn2Erc20Address, nil - } - - return nil, fmt.Errorf("unexpected source bytes") - }, + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + if string(sourceBytes) == "tkn1" { + return [][]byte{tkn1Erc20Address}, nil + } + if string(sourceBytes) == "tkn2" { + return [][]byte{tkn2Erc20Address}, nil + } + + return nil, fmt.Errorf("unexpected source bytes") } args.Erc20ContractsHolder = &bridge.ERC20ContractsHolderStub{ BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { diff --git a/testsCommon/bridge/dataGetterStub.go b/testsCommon/bridge/dataGetterStub.go index 5badf503..f3209009 100644 --- a/testsCommon/bridge/dataGetterStub.go +++ b/testsCommon/bridge/dataGetterStub.go @@ -9,6 +9,7 @@ type DataGetterStub struct { GetTokenIdForErc20AddressCalled func(ctx context.Context, erc20Address []byte) ([][]byte, error) GetERC20AddressForTokenIdCalled func(ctx context.Context, tokenId []byte) ([][]byte, error) GetAllStakedRelayersCalled func(ctx context.Context) ([][]byte, error) + GetAllKnownTokensCalled func(ctx context.Context) ([][]byte, error) } // GetTokenIdForErc20Address - @@ -36,6 +37,15 @@ func (stub *DataGetterStub) GetAllStakedRelayers(ctx context.Context) ([][]byte, return make([][]byte, 0), nil } +// GetAllKnownTokens - +func (stub *DataGetterStub) GetAllKnownTokens(ctx context.Context) ([][]byte, error) { + if stub.GetAllKnownTokensCalled != nil { + return stub.GetAllKnownTokensCalled(ctx) + } + + return make([][]byte, 0), nil +} + // IsInterfaceNil - func (stub *DataGetterStub) IsInterfaceNil() bool { return stub == nil From 5d860dc80b92b3df141c19add9e842ffee12dece Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Tue, 3 Sep 2024 17:55:01 +0300 Subject: [PATCH 06/15] - fixes --- cmd/migration/config/config.toml | 3 +- cmd/migration/config/migration.json | 33 ++++++++++--------- cmd/migration/flags.go | 2 +- cmd/migration/main.go | 21 ++++++++---- config/tomlConfigs_test.go | 4 ++- executors/ethereum/errors.go | 1 + executors/ethereum/migrationBatchCreator.go | 18 ++++++++++ .../ethereum/migrationBatchCreator_test.go | 12 +++++++ 8 files changed, 69 insertions(+), 25 deletions(-) diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml index c5c36513..e2e13976 100644 --- a/cmd/migration/config/config.toml +++ b/cmd/migration/config/config.toml @@ -20,12 +20,13 @@ [MultiversX] NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqhxkc48lt5uv2hejj4wtjqvugfm4wgv6gyfkqw0uuxl" [MultiversX.Proxy] CacherExpirationSeconds = 600 # the caching time in seconds # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) - RestAPIEntityType = "observer" + RestAPIEntityType = "proxy" FinalityCheck = true MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized diff --git a/cmd/migration/config/migration.json b/cmd/migration/config/migration.json index 3f03dc36..25d106fd 100644 --- a/cmd/migration/config/migration.json +++ b/cmd/migration/config/migration.json @@ -1,76 +1,77 @@ { "OldSafeContractAddress": "0x92A26975433A61CF1134802586aa669bAB8B69f3", - "NewSafeContractAddress": "0x37aaaf95887624a363effB7762D489E3C05c2a02", - "BatchID": 3547, + "NewSafeContractAddress": "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + "BatchID": 3548, + "MessageHash": "0x9fad9673d99127cf3bfbcb25fdb271c90009e1199e5ae3bd98f516a6d2b46357", "DepositsInfo": [ { - "DepositNonce": 4651, + "DepositNonce": 4652, "Token": "ETHUSDC-220753", "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "Amount": "7245293620507" + "Amount": "7091634513581" }, { - "DepositNonce": 4652, + "DepositNonce": 4653, "Token": "ETHUTK-8cdf7a", "ContractAddress": "0xdc9Ac3C20D1ed0B540dF9b1feDC10039Df13F99c", "Amount": "224564169411881824066539333" }, { - "DepositNonce": 4653, + "DepositNonce": 4654, "Token": "ETHUSDT-9c73c6", "ContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "Amount": "305146464594" + "Amount": "318715112062" }, { - "DepositNonce": 4654, + "DepositNonce": 4655, "Token": "ETHBUSD-450923", "ContractAddress": "0x4Fabb145d64652a948d72533023f6E7A623C7C53", "Amount": "22294352736330153151984" }, { - "DepositNonce": 4655, + "DepositNonce": 4656, "Token": "ETHHMT-18538a", "ContractAddress": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", "Amount": "435000000000000000000" }, { - "DepositNonce": 4656, + "DepositNonce": 4657, "Token": "ETHCGG-ee4e0c", "ContractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", "Amount": "1500138370967581082219795" }, { - "DepositNonce": 4657, + "DepositNonce": 4658, "Token": "ETHINFRA-60a3bf", "ContractAddress": "0x013062189dc3dcc99e9Cee714c513033b8d99e3c", "Amount": "141172595980399500424091" }, { - "DepositNonce": 4658, + "DepositNonce": 4659, "Token": "ETHWBTC-74e282", "ContractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", "Amount": "3898848955" }, { - "DepositNonce": 4659, + "DepositNonce": 4660, "Token": "ETHWETH-e1c126", "ContractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "Amount": "725921417141355037005" }, { - "DepositNonce": 4660, + "DepositNonce": 4661, "Token": "ETHWSDAI-572803", "ContractAddress": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", "Amount": "5431516086574385345409" }, { - "DepositNonce": 4661, + "DepositNonce": 4662, "Token": "ETHWDAI-bd65f9", "ContractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "Amount": "127608014625114134146189" }, { - "DepositNonce": 4662, + "DepositNonce": 4663, "Token": "ETHUMB-291202", "ContractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", "Amount": "4669367702477323508554075" diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go index 37b07063..e2fce5fa 100644 --- a/cmd/migration/flags.go +++ b/cmd/migration/flags.go @@ -15,7 +15,7 @@ var ( ", if set to *:INFO the logs for all packages will have the INFO level. However, if set to *:INFO,api:DEBUG" + " the logs for all packages will have the INFO level, excepting the api package which will receive a DEBUG" + " log level.", - Value: "*:" + logger.LogDebug.String(), + Value: "*:" + logger.LogInfo.String(), } configurationFile = cli.StringFlag{ Name: "config", diff --git a/cmd/migration/main.go b/cmd/migration/main.go index cf7c19a2..7b4c5cf8 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -57,6 +57,8 @@ func main() { log.Error(err.Error()) os.Exit(1) } + + log.Info("process finished successfully") } func execute(ctx *cli.Context) error { @@ -106,9 +108,14 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } + safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress) + if err != nil { + return err + } + argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ MultisigContractAddress: multisigAddress, - SafeContractAddress: dummyAddress, + SafeContractAddress: safeAddress, RelayerAddress: dummyAddress, Proxy: proxy, Log: log, @@ -143,6 +150,7 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { Erc20ContractsHolder: erc20ContractsHolder, SafeContractAddress: safeEthAddress, SafeContractWrapper: safeInstance, + Logger: log, } creator, err := ethereum.NewMigrationBatchCreator(argsCreator) @@ -171,16 +179,15 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } + log.Info("signing batch", "message hash", batchInfo.MessageHash.String(), + "public key", cryptoHandler.GetAddress().String()) + signature, err := cryptoHandler.Sign(batchInfo.MessageHash) if err != nil { return err } - log.Info(string(val)) - log.Info("Batch signed", - "public key", cryptoHandler.GetAddress().String(), - "message hash", batchInfo.MessageHash.String(), - "signature", signature) + log.Info("Migration .json file contents: \n" + string(val)) jsonFilename := ctx.GlobalString(migrationJsonFile.Name) err = os.WriteFile(jsonFilename, val, os.ModePerm) @@ -200,6 +207,8 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } + log.Info("Signature .json file contents: \n" + string(val)) + return os.WriteFile(sigFilename, val, os.ModePerm) } diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go index 03d1ce67..3f7d72e4 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -483,6 +483,7 @@ func TestMigrationToolConfig(t *testing.T) { MultiversX: MultiversXConfig{ NetworkAddress: "https://devnet-gateway.multiversx.com", MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", Proxy: ProxyConfig{ CacherExpirationSeconds: 600, RestAPIEntityType: "observer", @@ -518,7 +519,8 @@ func TestMigrationToolConfig(t *testing.T) { [MultiversX] NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address - MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" # the multiversx address for the bridge contract + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract [MultiversX.Proxy] CacherExpirationSeconds = 600 # the caching time in seconds diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index c0c19f9c..2706fde9 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -8,4 +8,5 @@ var ( errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") errNilSafeContractWrapper = errors.New("nil safe contract wrapper") errWrongERC20AddressResponse = errors.New("wrong ERC20 address response") + errNilLogger = errors.New("nil logger") ) diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index a7c4621c..680573da 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -4,12 +4,14 @@ import ( "context" "fmt" "math/big" + "strings" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" ) var zero = big.NewInt(0) @@ -20,6 +22,7 @@ type ArgsMigrationBatchCreator struct { Erc20ContractsHolder Erc20ContractsHolder SafeContractAddress common.Address SafeContractWrapper SafeContractWrapper + Logger logger.Logger } type migrationBatchCreator struct { @@ -27,6 +30,7 @@ type migrationBatchCreator struct { erc20ContractsHolder Erc20ContractsHolder safeContractAddress common.Address safeContractWrapper SafeContractWrapper + logger logger.Logger } // NewMigrationBatchCreator creates a new instance of type migrationBatchCreator that is able to generate the migration batch output file @@ -40,17 +44,23 @@ func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCr if check.IfNilReflect(args.SafeContractWrapper) { return nil, errNilSafeContractWrapper } + if check.IfNil(args.Logger) { + return nil, errNilLogger + } return &migrationBatchCreator{ mvxDataGetter: args.MvxDataGetter, erc20ContractsHolder: args.Erc20ContractsHolder, safeContractAddress: args.SafeContractAddress, safeContractWrapper: args.SafeContractWrapper, + logger: args.Logger, }, nil } // CreateBatchInfo creates an instance of type BatchInfo func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSafeAddress common.Address) (*BatchInfo, error) { + creator.logger.Info("started the batch creation process...") + batchesCount, err := creator.safeContractWrapper.BatchesCount(&bind.CallOpts{Context: ctx}) if err != nil { return nil, err @@ -61,21 +71,29 @@ func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSa return nil, err } + creator.logger.Info("fetched Ethereum contracts state", "batches count", batchesCount, "deposits count", depositsCount) + tokensList, err := creator.getTokensList(ctx) if err != nil { return nil, err } + creator.logger.Info("fetched known tokens", "tokens", strings.Join(tokensList, ", ")) + deposits, err := creator.fetchERC20ContractsAddresses(ctx, tokensList, depositsCount) if err != nil { return nil, err } + creator.logger.Info("fetched ERC20 contract addresses") + err = creator.fetchBalances(ctx, deposits) if err != nil { return nil, err } + creator.logger.Info("fetched balances contract addresses") + return creator.assembleBatchInfo(batchesCount, deposits, newSafeAddress) } diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go index 801e2465..77c97056 100644 --- a/executors/ethereum/migrationBatchCreator_test.go +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + "github.com/multiversx/mx-chain-go/testscommon" "github.com/stretchr/testify/assert" ) @@ -37,6 +38,7 @@ func createMockArgsForMigrationBatchCreator() ArgsMigrationBatchCreator { Erc20ContractsHolder: &bridge.ERC20ContractsHolderStub{}, SafeContractAddress: safeContractAddress, SafeContractWrapper: &bridge.SafeContractWrapperStub{}, + Logger: &testscommon.LoggerStub{}, } } @@ -73,6 +75,16 @@ func TestNewMigrationBatchCreator(t *testing.T) { assert.Nil(t, creator) assert.Equal(t, errNilSafeContractWrapper, err) }) + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.Logger = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilLogger, err) + }) t.Run("should work", func(t *testing.T) { t.Parallel() From 1a5f57d6f028c8655bb8362150444d1ed9a88995 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Wed, 4 Sep 2024 19:07:32 +0300 Subject: [PATCH 07/15] - fixes after review: renaming --- executors/ethereum/common.go | 12 +++++----- executors/ethereum/migrationBatchCreator.go | 20 ++++++++-------- .../ethereum/migrationBatchCreator_test.go | 24 +++++++++---------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go index 71f5e146..7732975d 100644 --- a/executors/ethereum/common.go +++ b/executors/ethereum/common.go @@ -8,12 +8,12 @@ import ( // DepositInfo is the deposit info list type DepositInfo struct { - DepositNonce uint64 `json:"DepositNonce"` - Token string `json:"Token"` - ContractAddress string `json:"ContractAddress"` - contractAddress common.Address - amount *big.Int - Amount string `json:"Amount"` + DepositNonce uint64 `json:"DepositNonce"` + Token string `json:"Token"` + ContractAddressString string `json:"ContractAddress"` + ContractAddress common.Address `json:"-"` + Amount *big.Int `json:"-"` + AmountString string `json:"Amount"` } // BatchInfo is the batch info list diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index bf2be86f..d5bae823 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -87,11 +87,11 @@ func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.C } deposit := &DepositInfo{ - DepositNonce: lastDepositNonce + uint64(1+idx), - Token: token, - ContractAddress: common.BytesToAddress(addressBytes).String(), - contractAddress: common.BytesToAddress(addressBytes), - Amount: "", + DepositNonce: lastDepositNonce + uint64(1+idx), + Token: token, + ContractAddressString: common.BytesToAddress(addressBytes).String(), + ContractAddress: common.BytesToAddress(addressBytes), + AmountString: "", } deposits = append(deposits, deposit) @@ -102,13 +102,13 @@ func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.C func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposits []*DepositInfo) error { for _, deposit := range deposits { - balance, err := creator.erc20ContractsHolder.BalanceOf(ctx, deposit.contractAddress, creator.safeContractAddress) + balance, err := creator.erc20ContractsHolder.BalanceOf(ctx, deposit.ContractAddress, creator.safeContractAddress) if err != nil { - return fmt.Errorf("%w for address %s in ERC20 contract %s", err, creator.safeContractAddress.String(), deposit.contractAddress.String()) + return fmt.Errorf("%w for address %s in ERC20 contract %s", err, creator.safeContractAddress.String(), deposit.ContractAddress.String()) } - deposit.amount = balance - deposit.Amount = balance.String() + deposit.Amount = balance + deposit.AmountString = balance.String() } return nil @@ -123,7 +123,7 @@ func (creator *migrationBatchCreator) assembleBatchInfo(batchesCount uint64, dep } for _, deposit := range deposits { - if deposit.amount.Cmp(zero) <= 0 { + if deposit.Amount.Cmp(zero) <= 0 { continue } diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go index b5edcba4..348a5235 100644 --- a/executors/ethereum/migrationBatchCreator_test.go +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -210,20 +210,20 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { BatchID: 2245, DepositsInfo: []*DepositInfo{ { - DepositNonce: 40, - Token: "tkn1", - ContractAddress: common.BytesToAddress(tkn1Erc20Address).String(), - contractAddress: common.BytesToAddress(tkn1Erc20Address), - amount: big.NewInt(37), - Amount: "37", + DepositNonce: 40, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(37), + AmountString: "37", }, { - DepositNonce: 41, - Token: "tkn2", - ContractAddress: common.BytesToAddress(tkn2Erc20Address).String(), - contractAddress: common.BytesToAddress(tkn2Erc20Address), - amount: big.NewInt(38), - Amount: "38", + DepositNonce: 41, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(38), + AmountString: "38", }, }, } From 67ea3dc41532dd7c6c2a74c8c686ca6d4973cff9 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Thu, 5 Sep 2024 15:23:07 +0300 Subject: [PATCH 08/15] - fixes after review - added unique identifiers for output files --- clients/errors.go | 2 +- clients/ethereum/client_test.go | 4 ++-- cmd/migration/flags.go | 10 ++++++++-- cmd/migration/main.go | 27 +++++++++++++++++++++------ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/clients/errors.go b/clients/errors.go index 021d3515..c4652e8c 100644 --- a/clients/errors.go +++ b/clients/errors.go @@ -36,6 +36,6 @@ var ( // ErrNoPendingBatchAvailable signals that no pending batch is available ErrNoPendingBatchAvailable = errors.New("no pending batch available") - // ErrNilCryptoHandler signals that a nil crypto handler key was provided + // ErrNilCryptoHandler signals that a nil crypto handler was provided ErrNilCryptoHandler = errors.New("nil crypto handler") ) diff --git a/clients/ethereum/client_test.go b/clients/ethereum/client_test.go index 48ea7fce..a13922c7 100644 --- a/clients/ethereum/client_test.go +++ b/clients/ethereum/client_test.go @@ -485,7 +485,7 @@ func TestClient_BroadcastSignatureForMessageHash(t *testing.T) { } args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ SignCalled: func(msgHash common.Hash) ([]byte, error) { - assert.Equal(t, msgHash.Bytes(), hash.Bytes()) + assert.Equal(t, hash.Bytes(), msgHash.Bytes()) return nil, expectedError }, } @@ -510,7 +510,7 @@ func TestClient_BroadcastSignatureForMessageHash(t *testing.T) { } args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ SignCalled: func(msgHash common.Hash) ([]byte, error) { - assert.Equal(t, msgHash.Bytes(), hash.Bytes()) + assert.Equal(t, hash.Bytes(), msgHash.Bytes()) return []byte(expectedSig), nil }, } diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go index 37b07063..c8eba2ff 100644 --- a/cmd/migration/flags.go +++ b/cmd/migration/flags.go @@ -30,8 +30,13 @@ var ( } migrationJsonFile = cli.StringFlag{ Name: "migration-file", - Usage: "The input or output .json file containing the migration data", - Value: path.Join(configPath, "migration.json"), + Usage: "The output .json file containing the migration data", + Value: path.Join(configPath, "migration-"+timestampPlaceholder+".json"), + } + signatureJsonFile = cli.StringFlag{ + Name: "signature-file", + Usage: "The output .json file containing the signature data", + Value: path.Join(configPath, publicKeyPlaceholder+"-"+timestampPlaceholder+".json"), } newSafeAddress = cli.StringFlag{ Name: "new-safe-address", @@ -46,6 +51,7 @@ func getFlags() []cli.Flag { configurationFile, mode, migrationJsonFile, + signatureJsonFile, newSafeAddress, } } diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 93bcd293..c3e66baa 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "os" - "path" "strings" "time" @@ -29,10 +28,12 @@ import ( ) const ( - filePathPlaceholder = "[path]" - signMode = "sign" - executeMode = "execute" - configPath = "config" + filePathPlaceholder = "[path]" + signMode = "sign" + executeMode = "execute" + configPath = "config" + timestampPlaceholder = "[timestamp]" + publicKeyPlaceholder = "[public-key]" ) var log = logger.GetOrCreate("main") @@ -190,6 +191,7 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { "signature", signature) jsonFilename := ctx.GlobalString(migrationJsonFile.Name) + jsonFilename = applyTimestamp(jsonFilename) err = os.WriteFile(jsonFilename, val, os.ModePerm) if err != nil { return err @@ -201,7 +203,9 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { Signature: hex.EncodeToString(signature), } - sigFilename := path.Join(configPath, fmt.Sprintf("%s.json", sigInfo.PublicKey)) + sigFilename := ctx.GlobalString(signatureJsonFile.Name) + sigFilename = applyTimestamp(sigFilename) + sigFilename = applyPublicKey(sigFilename, sigInfo.PublicKey) val, err = json.MarshalIndent(sigInfo, "", " ") if err != nil { return err @@ -219,3 +223,14 @@ func loadConfig(filepath string) (config.MigrationToolConfig, error) { return cfg, nil } + +func applyTimestamp(input string) string { + actualTimestamp := time.Now().Format("2006-01-02T15-04-05") + actualTimestamp = strings.Replace(actualTimestamp, "T", "-", 1) + + return strings.Replace(input, timestampPlaceholder, actualTimestamp, 1) +} + +func applyPublicKey(input string, publickey string) string { + return strings.Replace(input, publicKeyPlaceholder, publickey, 1) +} From a08de870627eeea26d7580027ec305c594f94ff4 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Thu, 5 Sep 2024 16:10:56 +0300 Subject: [PATCH 09/15] - fixes --- executors/ethereum/migrationBatchCreator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index 6d927690..1589a301 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -147,9 +147,9 @@ func (creator *migrationBatchCreator) computeMessageHash(batch *BatchInfo) (comm amounts := make([]*big.Int, 0, len(batch.DepositsInfo)) nonces := make([]*big.Int, 0, len(batch.DepositsInfo)) for _, deposit := range batch.DepositsInfo { - tokens = append(tokens, common.HexToAddress(deposit.ContractAddress)) + tokens = append(tokens, deposit.ContractAddress) recipients = append(recipients, common.HexToAddress(batch.NewSafeContractAddress)) - amounts = append(amounts, deposit.amount) + amounts = append(amounts, deposit.Amount) nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) } From d2761700ac0fa5f1498798efcb93d1fcadfdcdd0 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 6 Sep 2024 11:41:41 +0300 Subject: [PATCH 10/15] - minor config fix --- cmd/migration/config/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml index e2e13976..801ac522 100644 --- a/cmd/migration/config/config.toml +++ b/cmd/migration/config/config.toml @@ -26,7 +26,7 @@ # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) - RestAPIEntityType = "proxy" + RestAPIEntityType = "observer" FinalityCheck = true MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized From 2b97dfcca22aeadea8a22ece63bab45a750944bd Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 6 Sep 2024 12:25:12 +0300 Subject: [PATCH 11/15] - added migration executor implementation --- cmd/migration/main.go | 127 +++- executors/ethereum/common.go | 2 +- executors/ethereum/errors.go | 6 + executors/ethereum/interface.go | 28 + executors/ethereum/migrationBatchExecutor.go | 293 ++++++++ .../ethereum/migrationBatchExecutor_test.go | 711 ++++++++++++++++++ executors/ethereum/signatures.go | 63 ++ executors/ethereum/signatures_test.go | 29 + ...92020F2b668D543d8-2024-09-05-15-34-44.json | 5 + ...aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json | 5 + executors/ethereum/testdata/0xDirectory/dummy | 1 + .../testdata/0xbad-2024-09-05-15-34-44.json | 4 + .../migration-2024-09-05-15-34-44.json | 80 ++ 13 files changed, 1332 insertions(+), 22 deletions(-) create mode 100644 executors/ethereum/migrationBatchExecutor.go create mode 100644 executors/ethereum/migrationBatchExecutor_test.go create mode 100644 executors/ethereum/signatures.go create mode 100644 executors/ethereum/signatures_test.go create mode 100755 executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json create mode 100755 executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json create mode 100644 executors/ethereum/testdata/0xDirectory/dummy create mode 100755 executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json create mode 100755 executors/ethereum/testdata/migration-2024-09-05-15-34-44.json diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 7b4c5cf8..795f4f77 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -15,9 +15,13 @@ import ( "github.com/ethereum/go-ethereum/ethclient" ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/wrappers" + "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement" + "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement/factory" "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" "github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled" "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" chainCore "github.com/multiversx/mx-chain-core-go/core" logger "github.com/multiversx/mx-chain-logger-go" @@ -36,6 +40,12 @@ const ( var log = logger.GetOrCreate("main") +type internalComponents struct { + batch *ethereum.BatchInfo + cryptoHandler ethereumClient.CryptoHandler + ethClient *ethclient.Client +} + func main() { app := cli.NewApp() app.Name = "Funds migration CLI tool" @@ -79,15 +89,17 @@ func execute(ctx *cli.Context) error { operationMode := strings.ToLower(ctx.GlobalString(mode.Name)) switch operationMode { case signMode: - return generateAndSign(ctx, cfg) + + _, err = generateAndSign(ctx, cfg) + return err case executeMode: - //TODO: implement + return executeTransfer(ctx, cfg) } return fmt.Errorf("unknown execution mode: %s", operationMode) } -func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { +func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) (*internalComponents, error) { argsProxy := blockchain.ArgsProxy{ ProxyURL: cfg.MultiversX.NetworkAddress, SameScState: false, @@ -99,18 +111,18 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { } proxy, err := blockchain.NewProxy(argsProxy) if err != nil { - return err + return nil, err } dummyAddress := data.NewAddressFromBytes(bytes.Repeat([]byte{0x1}, 32)) multisigAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.MultisigContractAddress) if err != nil { - return err + return nil, err } safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress) if err != nil { - return err + return nil, err } argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ @@ -122,12 +134,12 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { } mxDataGetter, err := multiversx.NewMXClientDataGetter(argsMXClientDataGetter) if err != nil { - return err + return nil, err } ethClient, err := ethclient.Dial(cfg.Eth.NetworkAddress) if err != nil { - return err + return nil, err } argsContractsHolder := ethereumClient.ArgsErc20SafeContractsHolder{ @@ -136,13 +148,13 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { } erc20ContractsHolder, err := ethereumClient.NewErc20SafeContractsHolder(argsContractsHolder) if err != nil { - return err + return nil, err } safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress) safeInstance, err := contract.NewERC20Safe(safeEthAddress, ethClient) if err != nil { - return err + return nil, err } argsCreator := ethereum.ArgsMigrationBatchCreator{ @@ -155,28 +167,28 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { creator, err := ethereum.NewMigrationBatchCreator(argsCreator) if err != nil { - return err + return nil, err } newSafeAddressString := ctx.GlobalString(newSafeAddress.Name) if len(newSafeAddressString) == 0 { - return fmt.Errorf("invalid new safe address for Ethereum") + return nil, fmt.Errorf("invalid new safe address for Ethereum") } newSafeAddressValue := common.HexToAddress(ctx.GlobalString(newSafeAddress.Name)) batchInfo, err := creator.CreateBatchInfo(context.Background(), newSafeAddressValue) if err != nil { - return err + return nil, err } val, err := json.MarshalIndent(batchInfo, "", " ") if err != nil { - return err + return nil, err } cryptoHandler, err := ethereumClient.NewCryptoHandler(cfg.Eth.PrivateKeyFile) if err != nil { - return err + return nil, err } log.Info("signing batch", "message hash", batchInfo.MessageHash.String(), @@ -184,7 +196,7 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { signature, err := cryptoHandler.Sign(batchInfo.MessageHash) if err != nil { - return err + return nil, err } log.Info("Migration .json file contents: \n" + string(val)) @@ -192,24 +204,97 @@ func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) error { jsonFilename := ctx.GlobalString(migrationJsonFile.Name) err = os.WriteFile(jsonFilename, val, os.ModePerm) if err != nil { - return err + return nil, err } sigInfo := ðereum.SignatureInfo{ - PublicKey: cryptoHandler.GetAddress().String(), + Address: cryptoHandler.GetAddress().String(), MessageHash: batchInfo.MessageHash.String(), Signature: hex.EncodeToString(signature), } - sigFilename := path.Join(configPath, fmt.Sprintf("%s.json", sigInfo.PublicKey)) + sigFilename := path.Join(configPath, fmt.Sprintf("%s.json", sigInfo.Address)) val, err = json.MarshalIndent(sigInfo, "", " ") if err != nil { - return err + return nil, err } log.Info("Signature .json file contents: \n" + string(val)) - return os.WriteFile(sigFilename, val, os.ModePerm) + err = os.WriteFile(sigFilename, val, os.ModePerm) + if err != nil { + return nil, err + } + + return &internalComponents{ + batch: batchInfo, + cryptoHandler: cryptoHandler, + ethClient: ethClient, + }, nil +} + +func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error { + components, err := generateAndSign(ctx, cfg) + if err != nil { + return err + } + + bridgeEthAddress := common.HexToAddress(cfg.Eth.MultisigContractAddress) + multiSigInstance, err := contract.NewBridge(bridgeEthAddress, components.ethClient) + if err != nil { + return err + } + + safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress) + safeInstance, err := contract.NewERC20Safe(safeEthAddress, components.ethClient) + if err != nil { + return err + } + + argsClientWrapper := wrappers.ArgsEthereumChainWrapper{ + StatusHandler: &disabled.StatusHandler{}, + MultiSigContract: multiSigInstance, + SafeContract: safeInstance, + BlockchainClient: components.ethClient, + } + ethereumChainWrapper, err := wrappers.NewEthereumChainWrapper(argsClientWrapper) + if err != nil { + return err + } + + gasStationConfig := cfg.Eth.GasStation + argsGasStation := gasManagement.ArgsGasStation{ + RequestURL: gasStationConfig.URL, + RequestPollingInterval: time.Duration(gasStationConfig.PollingIntervalInSeconds) * time.Second, + RequestRetryDelay: time.Duration(gasStationConfig.RequestRetryDelayInSeconds) * time.Second, + MaximumFetchRetries: gasStationConfig.MaxFetchRetries, + RequestTime: time.Duration(gasStationConfig.RequestTimeInSeconds) * time.Second, + MaximumGasPrice: gasStationConfig.MaximumAllowedGasPrice, + GasPriceSelector: core.EthGasPriceSelector(gasStationConfig.GasPriceSelector), + GasPriceMultiplier: gasStationConfig.GasPriceMultiplier, + } + gs, err := factory.CreateGasStation(argsGasStation, gasStationConfig.Enabled) + if err != nil { + return err + } + + args := ethereum.ArgsMigrationBatchExecutor{ + EthereumChainWrapper: ethereumChainWrapper, + CryptoHandler: components.cryptoHandler, + Batch: *components.batch, + Signatures: ethereum.LoadAllSignatures(log, configPath), + Logger: log, + GasHandler: gs, + TransferGasLimitBase: cfg.Eth.GasLimitBase, + TransferGasLimitForEach: cfg.Eth.GasLimitForEach, + } + + executor, err := ethereum.NewMigrationBatchExecutor(args) + if err != nil { + return err + } + + return executor.ExecuteTransfer(context.Background()) } func loadConfig(filepath string) (config.MigrationToolConfig, error) { diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go index 75902890..52e8a7c9 100644 --- a/executors/ethereum/common.go +++ b/executors/ethereum/common.go @@ -27,7 +27,7 @@ type BatchInfo struct { // SignatureInfo is the struct holding signature info type SignatureInfo struct { - PublicKey string `json:"PublicKey"` + Address string `json:"Address"` MessageHash string `json:"MessageHash"` Signature string `json:"Signature"` } diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go index 2706fde9..09d6b8a7 100644 --- a/executors/ethereum/errors.go +++ b/executors/ethereum/errors.go @@ -9,4 +9,10 @@ var ( errNilSafeContractWrapper = errors.New("nil safe contract wrapper") errWrongERC20AddressResponse = errors.New("wrong ERC20 address response") errNilLogger = errors.New("nil logger") + errNilCryptoHandler = errors.New("nil crypto handler") + errNilEthereumChainWrapper = errors.New("nil Ethereum chain wrapper") + errQuorumNotReached = errors.New("quorum not reached") + errInvalidSignature = errors.New("invalid signature") + errMultisigContractPaused = errors.New("multisig contract paused") + errNilGasHandler = errors.New("nil gas handler") ) diff --git a/executors/ethereum/interface.go b/executors/ethereum/interface.go index 5c3bc0f4..bf2a7a2f 100644 --- a/executors/ethereum/interface.go +++ b/executors/ethereum/interface.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ) // TokensMapper can convert a token bytes from one chain to another @@ -32,3 +33,30 @@ type MvxDataGetter interface { GetERC20AddressForTokenId(ctx context.Context, tokenId []byte) ([][]byte, error) IsInterfaceNil() bool } + +// EthereumChainWrapper defines the operations of the Ethereum wrapper +type EthereumChainWrapper interface { + ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, + recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, + signatures [][]byte) (*types.Transaction, error) + ChainID(ctx context.Context) (*big.Int, error) + BlockNumber(ctx context.Context) (uint64, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + Quorum(ctx context.Context) (*big.Int, error) + GetRelayers(ctx context.Context) ([]common.Address, error) + IsPaused(ctx context.Context) (bool, error) +} + +// CryptoHandler defines the operations for a component that expose some crypto primitives +type CryptoHandler interface { + Sign(msgHash common.Hash) ([]byte, error) + GetAddress() common.Address + CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) + IsInterfaceNil() bool +} + +// GasHandler defines the component able to fetch the current gas price +type GasHandler interface { + GetCurrentGasPrice() (*big.Int, error) + IsInterfaceNil() bool +} diff --git a/executors/ethereum/migrationBatchExecutor.go b/executors/ethereum/migrationBatchExecutor.go new file mode 100644 index 00000000..4df7b47b --- /dev/null +++ b/executors/ethereum/migrationBatchExecutor.go @@ -0,0 +1,293 @@ +package ethereum + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" +) + +const ethSignatureSize = 64 + +// ArgsMigrationBatchExecutor is the argument for the NewMigrationBatchExecutor constructor +type ArgsMigrationBatchExecutor struct { + EthereumChainWrapper EthereumChainWrapper + CryptoHandler CryptoHandler + Batch BatchInfo + Signatures []SignatureInfo + Logger logger.Logger + GasHandler GasHandler + TransferGasLimitBase uint64 + TransferGasLimitForEach uint64 +} + +type migrationBatchExecutor struct { + ethereumChainWrapper EthereumChainWrapper + cryptoHandler CryptoHandler + batch BatchInfo + signatures []SignatureInfo + logger logger.Logger + gasHandler GasHandler + transferGasLimitBase uint64 + transferGasLimitForEach uint64 +} + +// NewMigrationBatchExecutor creates a new instance of type migrationBatchCreator that is able to execute the multisig transfer +func NewMigrationBatchExecutor(args ArgsMigrationBatchExecutor) (*migrationBatchExecutor, error) { + if check.IfNilReflect(args.EthereumChainWrapper) { + return nil, errNilEthereumChainWrapper + } + if check.IfNil(args.CryptoHandler) { + return nil, errNilCryptoHandler + } + if check.IfNil(args.Logger) { + return nil, errNilLogger + } + if check.IfNil(args.GasHandler) { + return nil, errNilGasHandler + } + + return &migrationBatchExecutor{ + ethereumChainWrapper: args.EthereumChainWrapper, + cryptoHandler: args.CryptoHandler, + batch: args.Batch, + signatures: args.Signatures, + logger: args.Logger, + gasHandler: args.GasHandler, + transferGasLimitBase: args.TransferGasLimitBase, + transferGasLimitForEach: args.TransferGasLimitForEach, + }, nil +} + +// ExecuteTransfer will try to execute the transfer +func (executor *migrationBatchExecutor) ExecuteTransfer(ctx context.Context) error { + isPaused, err := executor.ethereumChainWrapper.IsPaused(ctx) + if err != nil { + return fmt.Errorf("%w in executor.ExecuteTransfer", err) + } + if isPaused { + return fmt.Errorf("%w in executor.ExecuteTransfer", errMultisigContractPaused) + } + + relayers, err := executor.ethereumChainWrapper.GetRelayers(ctx) + if err != nil { + return err + } + + quorum, err := executor.ethereumChainWrapper.Quorum(ctx) + if err != nil { + return err + } + + signatures, err := executor.checkRelayersSigsAndQuorum(relayers, quorum) + if err != nil { + return err + } + + nonce, err := executor.getNonce(ctx, executor.cryptoHandler.GetAddress()) + if err != nil { + return err + } + + chainId, err := executor.ethereumChainWrapper.ChainID(ctx) + if err != nil { + return err + } + + auth, err := executor.cryptoHandler.CreateKeyedTransactor(chainId) + if err != nil { + return err + } + + gasPrice, err := executor.gasHandler.GetCurrentGasPrice() + if err != nil { + return err + } + + tokens, recipients, amounts, depositNonces, batchNonce := executor.extractArgumentsFromBatch() + + auth.Nonce = big.NewInt(nonce) + auth.Value = big.NewInt(0) + auth.GasLimit = executor.transferGasLimitBase + uint64(len(tokens))*executor.transferGasLimitForEach + auth.Context = ctx + auth.GasPrice = gasPrice + + tx, err := executor.ethereumChainWrapper.ExecuteTransfer(auth, tokens, recipients, amounts, depositNonces, batchNonce, signatures) + if err != nil { + return err + } + + txHash := tx.Hash().String() + executor.logger.Info("Executed transfer transaction", "batchID", executor.batch.BatchID, "hash", txHash) + + return nil +} + +func (executor *migrationBatchExecutor) getNonce(ctx context.Context, fromAddress common.Address) (int64, error) { + blockNonce, err := executor.ethereumChainWrapper.BlockNumber(ctx) + if err != nil { + return 0, fmt.Errorf("%w in getNonce, BlockNumber call", err) + } + + nonce, err := executor.ethereumChainWrapper.NonceAt(ctx, fromAddress, big.NewInt(int64(blockNonce))) + + return int64(nonce), err +} + +func (executor *migrationBatchExecutor) extractArgumentsFromBatch() ( + tokens []common.Address, + recipients []common.Address, + amounts []*big.Int, + nonces []*big.Int, + batchNonce *big.Int, +) { + tokens = make([]common.Address, 0, len(executor.batch.DepositsInfo)) + recipients = make([]common.Address, 0, len(executor.batch.DepositsInfo)) + amounts = make([]*big.Int, 0, len(executor.batch.DepositsInfo)) + nonces = make([]*big.Int, 0, len(executor.batch.DepositsInfo)) + batchNonce = big.NewInt(0).SetUint64(executor.batch.BatchID) + + newSafeContractAddress := common.HexToAddress(executor.batch.NewSafeContractAddress) + for _, deposit := range executor.batch.DepositsInfo { + tokens = append(tokens, deposit.contractAddress) + recipients = append(recipients, newSafeContractAddress) + amounts = append(amounts, deposit.amount) + nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) + } + + return +} + +func (executor *migrationBatchExecutor) checkRelayersSigsAndQuorum(relayers []common.Address, quorum *big.Int) ([][]byte, error) { + sameMessageHashSignatures := executor.getSameMessageHashSignatures() + validSignatures := executor.getValidSignatures(sameMessageHashSignatures) + return executor.checkQuorum(relayers, quorum, validSignatures) +} + +func (executor *migrationBatchExecutor) getSameMessageHashSignatures() []SignatureInfo { + filtered := make([]SignatureInfo, 0, len(executor.signatures)) + for _, sigInfo := range executor.signatures { + if sigInfo.MessageHash != executor.batch.MessageHash.String() { + executor.logger.Warn("found a signature info that was not carried on the same message hash", + "local message hash", executor.batch.MessageHash.String(), + "address", sigInfo.Address, "message hash", sigInfo.MessageHash) + + continue + } + + filtered = append(filtered, sigInfo) + } + + return filtered +} + +func (executor *migrationBatchExecutor) getValidSignatures(provided []SignatureInfo) []SignatureInfo { + filtered := make([]SignatureInfo, 0, len(provided)) + for _, sigInfo := range provided { + hash := common.HexToHash(sigInfo.MessageHash) + sig, err := hex.DecodeString(sigInfo.Signature) + if err != nil { + executor.logger.Warn("found a non valid signature info (can not unhex the signature)", + "address", sigInfo.Address, "message hash", sigInfo.MessageHash, "signature", sigInfo.Signature, "error", err) + continue + } + + err = verifySignature(hash, sig, common.HexToAddress(sigInfo.Address)) + if err != nil { + executor.logger.Warn("found a non valid signature info", + "address", sigInfo.Address, "message hash", sigInfo.MessageHash, "signature", sigInfo.Signature, "error", err) + continue + } + + filtered = append(filtered, sigInfo) + } + + return filtered +} + +func verifySignature(messageHash common.Hash, signature []byte, address common.Address) error { + pkBytes, err := crypto.Ecrecover(messageHash.Bytes(), signature) + if err != nil { + return err + } + + pk, err := crypto.UnmarshalPubkey(pkBytes) + if err != nil { + return err + } + + addressFromPk := crypto.PubkeyToAddress(*pk) + if addressFromPk.String() != address.String() { + // we need to check that the recovered public key matched the one provided in order to make sure + // that the signature, hash and public key match + return errInvalidSignature + } + + if len(signature) > ethSignatureSize { + // signatures might contain the recovery byte + signature = signature[:ethSignatureSize] + } + + sigOk := crypto.VerifySignature(pkBytes, messageHash.Bytes(), signature) + if !sigOk { + return errInvalidSignature + } + + return nil +} + +func (executor *migrationBatchExecutor) checkQuorum(relayers []common.Address, quorum *big.Int, signatures []SignatureInfo) ([][]byte, error) { + whitelistedRelayers := make(map[common.Address]SignatureInfo) + + for _, sigInfo := range signatures { + if !isWhitelistedRelayer(sigInfo, relayers) { + executor.logger.Warn("found a non whitelisted relayer", + "address", sigInfo.Address) + continue + } + + relayerAddress := common.HexToAddress(sigInfo.Address) + _, found := whitelistedRelayers[relayerAddress] + if found { + executor.logger.Warn("found a multiple relayer sig info, ignoring", + "address", sigInfo.Address) + continue + } + + whitelistedRelayers[relayerAddress] = sigInfo + } + + result := make([][]byte, 0, len(whitelistedRelayers)) + for _, sigInfo := range whitelistedRelayers { + sig, err := hex.DecodeString(sigInfo.Signature) + if err != nil { + return nil, fmt.Errorf("internal error: %w while decoding this string %s that should have been hexed encoded", err, sigInfo.Signature) + } + + result = append(result, sig) + executor.logger.Info("valid signature recorded for whitelisted relayer", "relayer", sigInfo.Address) + } + + if uint64(len(result)) < quorum.Uint64() { + return nil, fmt.Errorf("%w: minimum %d, got %d", errQuorumNotReached, quorum.Uint64(), len(result)) + } + + return result, nil +} + +func isWhitelistedRelayer(sigInfo SignatureInfo, relayers []common.Address) bool { + relayerAddress := common.HexToAddress(sigInfo.Address) + for _, relayer := range relayers { + if bytes.Equal(relayer.Bytes(), relayerAddress.Bytes()) { + return true + } + } + + return false +} diff --git a/executors/ethereum/migrationBatchExecutor_test.go b/executors/ethereum/migrationBatchExecutor_test.go new file mode 100644 index 00000000..a192cd72 --- /dev/null +++ b/executors/ethereum/migrationBatchExecutor_test.go @@ -0,0 +1,711 @@ +package ethereum + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "errors" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var log = logger.GetOrCreate("executors/ethereum_test") + +func createMockArgsMigrationBatchExecutor() ArgsMigrationBatchExecutor { + return ArgsMigrationBatchExecutor{ + EthereumChainWrapper: &bridge.EthereumClientWrapperStub{}, + CryptoHandler: &bridge.CryptoHandlerStub{}, + Batch: BatchInfo{}, + Signatures: make([]SignatureInfo, 0), + Logger: log, + GasHandler: &testsCommon.GasHandlerStub{}, + TransferGasLimitBase: 100, + TransferGasLimitForEach: 10, + } +} + +func createPrivateKeys(tb testing.TB, num int) []*ecdsa.PrivateKey { + keys := make([]*ecdsa.PrivateKey, 0, num) + + for i := 0; i < num; i++ { + skBytes := make([]byte, 32) + _, _ = rand.Read(skBytes) + + privateKey, err := ethCrypto.HexToECDSA(hex.EncodeToString(skBytes)) + require.Nil(tb, err) + + keys = append(keys, privateKey) + } + + return keys +} + +func sign(tb testing.TB, sk *ecdsa.PrivateKey, msgHash common.Hash) []byte { + sig, err := ethCrypto.Sign(msgHash.Bytes(), sk) + require.Nil(tb, err) + + return sig +} + +func TestNewMigrationBatchExecutor(t *testing.T) { + t.Parallel() + + t.Run("nil Ethereum chain wrapper should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.EthereumChainWrapper = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilEthereumChainWrapper, err) + }) + t.Run("nil crypto handler should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.CryptoHandler = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilCryptoHandler, err) + }) + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Logger = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilLogger, err) + }) + t.Run("nil gas handler should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.GasHandler = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilGasHandler, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + executor, err := NewMigrationBatchExecutor(args) + assert.NotNil(t, executor) + assert.Nil(t, err) + }) +} + +func TestMigrationBatchExecutor_checkRelayersSigsAndQuorum(t *testing.T) { + t.Parallel() + + t.Run("quorum not satisfied should error", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 3) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(4)) + assert.ErrorIs(t, err, errQuorumNotReached) + assert.Contains(t, err.Error(), "minimum 4, got 3") + assert.Empty(t, verifiedSigs) + }) + t.Run("should work with wrong sig info elements", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 6) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + wrongMsgHash := common.HexToHash(strings.Repeat("2", 64)) + + correctSigForFifthElement := hex.EncodeToString(sign(t, privateKeys[5], testMsgHash)) + signatures := []SignatureInfo{ + // wrong message hash + { + Address: ethCrypto.PubkeyToAddress(privateKeys[4].PublicKey).String(), + MessageHash: wrongMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[4], wrongMsgHash)), + }, + // wrong signature: another message hash + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[5], wrongMsgHash)), + }, + // wrong signature: not a hex string + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: "not a hex string", + }, + // wrong signature: malformed signature + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: strings.Replace(correctSigForFifthElement, "1", "2", -1), + }, + // repeated good sig[1] + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + // good sigs + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[3].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[3], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + // all but private key[3] are whitelisted + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[4].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(3)) + assert.Nil(t, err) + assert.Equal(t, 3, len(verifiedSigs)) + }) + t.Run("should work with correct sig elements", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 3) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(3)) + assert.Nil(t, err) + assert.Equal(t, 3, len(verifiedSigs)) + }) +} + +func TestMigrationBatchExecutor_ExecuteTransfer(t *testing.T) { + t.Parallel() + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + newSafeContractAddress := common.HexToAddress("A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c") + batchInfo := BatchInfo{ + OldSafeContractAddress: "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: 4432, + MessageHash: testMsgHash, + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 37, + Token: "tkn1", + ContractAddress: common.BytesToAddress(tkn1Erc20Address).String(), + contractAddress: common.BytesToAddress(tkn1Erc20Address), + amount: big.NewInt(112), + Amount: "112", + }, + { + DepositNonce: 38, + Token: "tkn2", + ContractAddress: common.BytesToAddress(tkn2Erc20Address).String(), + contractAddress: common.BytesToAddress(tkn2Erc20Address), + amount: big.NewInt(113), + Amount: "113", + }, + }, + } + + privateKeys := createPrivateKeys(t, 3) + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + testBlockNumber := uint64(1000000) + senderNonce := uint64(3377) + testChainId := big.NewInt(2222) + testGasPrice := big.NewInt(112233) + + expectedTokens := []common.Address{ + common.BytesToAddress(tkn1Erc20Address), + common.BytesToAddress(tkn2Erc20Address), + } + expectedRecipients := []common.Address{ + newSafeContractAddress, + newSafeContractAddress, + } + expectedAmounts := []*big.Int{ + big.NewInt(112), + big.NewInt(113), + } + expectedNonces := []*big.Int{ + big.NewInt(37), + big.NewInt(38), + } + expectedSignatures := make([][]byte, 0, len(signatures)) + for _, sigInfo := range signatures { + sig, err := hex.DecodeString(sigInfo.Signature) + require.Nil(t, err) + expectedSignatures = append(expectedSignatures, sig) + } + expectedErr := errors.New("expected error") + + t.Run("is paused query errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + IsPausedCalled: func(ctx context.Context) (bool, error) { + return false, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("is paused should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + IsPausedCalled: func(ctx context.Context) (bool, error) { + return true, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, errMultisigContractPaused) + }) + t.Run("get relayers errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.Equal(t, expectedErr, err) + }) + t.Run("get quorum errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.Equal(t, expectedErr, err) + }) + t.Run("checking the signatures and relayers errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures // no whitelisted relayers + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, errQuorumNotReached) + }) + t.Run("get block errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + return 0, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("get nonce errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return 0, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("chain ID errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("create keyed transactor errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return nil, expectedErr + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("get gas price errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } + args.GasHandler = &testsCommon.GasHandlerStub{ + GetCurrentGasPriceCalled: func() (*big.Int, error) { + return nil, expectedErr + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("execute transfer errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + return nil, expectedErr + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + executeWasCalled := false + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + return testBlockNumber, nil + }, + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + assert.Equal(t, big.NewInt(0).SetUint64(testBlockNumber), blockNumber) + return senderNonce, nil + }, + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + return testChainId, nil + }, + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Equal(t, big.NewInt(0).SetUint64(senderNonce), opts.Nonce) + assert.Equal(t, big.NewInt(0), opts.Value) + assert.Equal(t, uint64(100+10+10), opts.GasLimit) // base + 2 deposits + assert.Equal(t, testGasPrice, opts.GasPrice) + assert.Equal(t, expectedTokens, tokens) + assert.Equal(t, expectedRecipients, recipients) + assert.Equal(t, expectedAmounts, amounts) + assert.Equal(t, expectedNonces, nonces) + assert.Equal(t, big.NewInt(4432), batchNonce) + assert.ElementsMatch(t, expectedSignatures, signatures) + executeWasCalled = true + + txData := &types.LegacyTx{ + Nonce: 0, + Data: []byte("mocked data"), + } + tx := types.NewTx(txData) + + return tx, nil + }, + } + args.GasHandler = &testsCommon.GasHandlerStub{ + GetCurrentGasPriceCalled: func() (*big.Int, error) { + return testGasPrice, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + assert.Equal(t, testChainId, chainId) + return &bind.TransactOpts{}, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + + err := executor.ExecuteTransfer(context.Background()) + assert.Nil(t, err) + assert.True(t, executeWasCalled) + }) +} diff --git a/executors/ethereum/signatures.go b/executors/ethereum/signatures.go new file mode 100644 index 00000000..54fadf75 --- /dev/null +++ b/executors/ethereum/signatures.go @@ -0,0 +1,63 @@ +package ethereum + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + + logger "github.com/multiversx/mx-chain-logger-go" +) + +const filesPattern = "0x*.json" + +// LoadAllSignatures can load all valid signatures from the specified directory +func LoadAllSignatures(logger logger.Logger, path string) []SignatureInfo { + filesContents, err := getAllFilesContents(path) + if err != nil { + logger.Warn(err.Error()) + return make([]SignatureInfo, 0) + } + + signatures := make([]SignatureInfo, 0, len(filesContents)) + for _, buff := range filesContents { + sigInfo := &SignatureInfo{} + err = json.Unmarshal(buff, sigInfo) + if err != nil { + logger.Warn("error unmarshalling to json", "error", err) + continue + } + + signatures = append(signatures, *sigInfo) + } + + return signatures +} + +func getAllFilesContents(dirPath string) ([][]byte, error) { + dirInfo, err := os.ReadDir(dirPath) + if err != nil { + return nil, fmt.Errorf("%w while fetching %s directory contents", err, dirPath) + } + + data := make([][]byte, 0, len(dirInfo)) + for _, di := range dirInfo { + if di.IsDir() { + continue + } + matched, errMatched := filepath.Match(filesPattern, di.Name()) + if errMatched != nil || !matched { + continue + } + + buff, errRead := os.ReadFile(path.Join(dirPath, di.Name())) + if errRead != nil { + continue + } + + data = append(data, buff) + } + + return data, nil +} diff --git a/executors/ethereum/signatures_test.go b/executors/ethereum/signatures_test.go new file mode 100644 index 00000000..de284867 --- /dev/null +++ b/executors/ethereum/signatures_test.go @@ -0,0 +1,29 @@ +package ethereum + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadAllSignatures(t *testing.T) { + t.Parallel() + + dirPath := "testdata" + sigs := LoadAllSignatures(log, dirPath) + + expectedSigs := []SignatureInfo{ + { + Address: "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", + MessageHash: "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + Signature: "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201", + }, + { + Address: "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + MessageHash: "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + Signature: "111222333", + }, + } + + assert.Equal(t, expectedSigs, sigs) +} diff --git a/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json new file mode 100755 index 00000000..091a7b7b --- /dev/null +++ b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json @@ -0,0 +1,5 @@ +{ + "Address": "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201" +} \ No newline at end of file diff --git a/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json new file mode 100755 index 00000000..b446e750 --- /dev/null +++ b/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json @@ -0,0 +1,5 @@ +{ + "Address": "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "111222333" +} diff --git a/executors/ethereum/testdata/0xDirectory/dummy b/executors/ethereum/testdata/0xDirectory/dummy new file mode 100644 index 00000000..c858c4e3 --- /dev/null +++ b/executors/ethereum/testdata/0xDirectory/dummy @@ -0,0 +1 @@ +dummy, empty file \ No newline at end of file diff --git a/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json new file mode 100755 index 00000000..03b372e7 --- /dev/null +++ b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json @@ -0,0 +1,4 @@ +{ + "Address": "0x3F", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201 \ No newline at end of file diff --git a/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json new file mode 100755 index 00000000..0c6f411d --- /dev/null +++ b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json @@ -0,0 +1,80 @@ +{ + "OldSafeContractAddress": "0x92A26975433A61CF1134802586aa669bAB8B69f3", + "NewSafeContractAddress": "0x1Ff78EB04d44a803E73c44FEf8790c5cAbD14596", + "BatchID": 3548, + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "DepositsInfo": [ + { + "DepositNonce": 4652, + "Token": "ETHUSDC-220753", + "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "Amount": "7086984513581" + }, + { + "DepositNonce": 4653, + "Token": "ETHUTK-8cdf7a", + "ContractAddress": "0xdc9Ac3C20D1ed0B540dF9b1feDC10039Df13F99c", + "Amount": "224564169411881824066539333" + }, + { + "DepositNonce": 4654, + "Token": "ETHUSDT-9c73c6", + "ContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "Amount": "318715112062" + }, + { + "DepositNonce": 4655, + "Token": "ETHBUSD-450923", + "ContractAddress": "0x4Fabb145d64652a948d72533023f6E7A623C7C53", + "Amount": "22294352736330153151984" + }, + { + "DepositNonce": 4656, + "Token": "ETHHMT-18538a", + "ContractAddress": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", + "Amount": "435000000000000000000" + }, + { + "DepositNonce": 4657, + "Token": "ETHCGG-ee4e0c", + "ContractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", + "Amount": "1500138370967581082219795" + }, + { + "DepositNonce": 4658, + "Token": "ETHINFRA-60a3bf", + "ContractAddress": "0x013062189dc3dcc99e9Cee714c513033b8d99e3c", + "Amount": "141172595980399500424091" + }, + { + "DepositNonce": 4659, + "Token": "ETHWBTC-74e282", + "ContractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "Amount": "3898848955" + }, + { + "DepositNonce": 4660, + "Token": "ETHWETH-e1c126", + "ContractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "Amount": "725921417141355037005" + }, + { + "DepositNonce": 4661, + "Token": "ETHWSDAI-572803", + "ContractAddress": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", + "Amount": "5431516086574385345409" + }, + { + "DepositNonce": 4662, + "Token": "ETHWDAI-bd65f9", + "ContractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "Amount": "127608014625114134146189" + }, + { + "DepositNonce": 4663, + "Token": "ETHUMB-291202", + "ContractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", + "Amount": "4669367702477323508554075" + } + ] +} \ No newline at end of file From ed52cc49eaf8411328fc2b8701fc300e83fa767c Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 6 Sep 2024 12:41:14 +0300 Subject: [PATCH 12/15] - fixes after merge --- executors/ethereum/migrationBatchExecutor.go | 4 ++-- .../ethereum/migrationBatchExecutor_test.go | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/executors/ethereum/migrationBatchExecutor.go b/executors/ethereum/migrationBatchExecutor.go index 4df7b47b..6a2e04ab 100644 --- a/executors/ethereum/migrationBatchExecutor.go +++ b/executors/ethereum/migrationBatchExecutor.go @@ -155,9 +155,9 @@ func (executor *migrationBatchExecutor) extractArgumentsFromBatch() ( newSafeContractAddress := common.HexToAddress(executor.batch.NewSafeContractAddress) for _, deposit := range executor.batch.DepositsInfo { - tokens = append(tokens, deposit.contractAddress) + tokens = append(tokens, deposit.ContractAddress) recipients = append(recipients, newSafeContractAddress) - amounts = append(amounts, deposit.amount) + amounts = append(amounts, deposit.Amount) nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) } diff --git a/executors/ethereum/migrationBatchExecutor_test.go b/executors/ethereum/migrationBatchExecutor_test.go index a192cd72..dcc89094 100644 --- a/executors/ethereum/migrationBatchExecutor_test.go +++ b/executors/ethereum/migrationBatchExecutor_test.go @@ -297,20 +297,20 @@ func TestMigrationBatchExecutor_ExecuteTransfer(t *testing.T) { MessageHash: testMsgHash, DepositsInfo: []*DepositInfo{ { - DepositNonce: 37, - Token: "tkn1", - ContractAddress: common.BytesToAddress(tkn1Erc20Address).String(), - contractAddress: common.BytesToAddress(tkn1Erc20Address), - amount: big.NewInt(112), - Amount: "112", + DepositNonce: 37, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(112), + AmountString: "112", }, { - DepositNonce: 38, - Token: "tkn2", - ContractAddress: common.BytesToAddress(tkn2Erc20Address).String(), - contractAddress: common.BytesToAddress(tkn2Erc20Address), - amount: big.NewInt(113), - Amount: "113", + DepositNonce: 38, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(113), + AmountString: "113", }, }, } From d652f62a3d52bafdca44d89b404f3433e87ab3d5 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 6 Sep 2024 12:53:16 +0300 Subject: [PATCH 13/15] - fixes in test files --- ...92020F2b668D543d8-2024-09-05-15-34-44.json | 2 +- executors/ethereum/testdata/0xDirectory/dummy | 2 +- .../testdata/0xbad-2024-09-05-15-34-44.json | 2 +- .../migration-2024-09-05-15-34-44.json | 68 +------------------ 4 files changed, 4 insertions(+), 70 deletions(-) diff --git a/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json index 091a7b7b..9cc62245 100755 --- a/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json +++ b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json @@ -2,4 +2,4 @@ "Address": "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201" -} \ No newline at end of file +} diff --git a/executors/ethereum/testdata/0xDirectory/dummy b/executors/ethereum/testdata/0xDirectory/dummy index c858c4e3..e977405b 100644 --- a/executors/ethereum/testdata/0xDirectory/dummy +++ b/executors/ethereum/testdata/0xDirectory/dummy @@ -1 +1 @@ -dummy, empty file \ No newline at end of file +dummy, empty file diff --git a/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json index 03b372e7..eab6c718 100755 --- a/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json +++ b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json @@ -1,4 +1,4 @@ { "Address": "0x3F", "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", - "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201 \ No newline at end of file + "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201 diff --git a/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json index 0c6f411d..450dae06 100755 --- a/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json +++ b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json @@ -9,72 +9,6 @@ "Token": "ETHUSDC-220753", "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "Amount": "7086984513581" - }, - { - "DepositNonce": 4653, - "Token": "ETHUTK-8cdf7a", - "ContractAddress": "0xdc9Ac3C20D1ed0B540dF9b1feDC10039Df13F99c", - "Amount": "224564169411881824066539333" - }, - { - "DepositNonce": 4654, - "Token": "ETHUSDT-9c73c6", - "ContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "Amount": "318715112062" - }, - { - "DepositNonce": 4655, - "Token": "ETHBUSD-450923", - "ContractAddress": "0x4Fabb145d64652a948d72533023f6E7A623C7C53", - "Amount": "22294352736330153151984" - }, - { - "DepositNonce": 4656, - "Token": "ETHHMT-18538a", - "ContractAddress": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", - "Amount": "435000000000000000000" - }, - { - "DepositNonce": 4657, - "Token": "ETHCGG-ee4e0c", - "ContractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", - "Amount": "1500138370967581082219795" - }, - { - "DepositNonce": 4658, - "Token": "ETHINFRA-60a3bf", - "ContractAddress": "0x013062189dc3dcc99e9Cee714c513033b8d99e3c", - "Amount": "141172595980399500424091" - }, - { - "DepositNonce": 4659, - "Token": "ETHWBTC-74e282", - "ContractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "Amount": "3898848955" - }, - { - "DepositNonce": 4660, - "Token": "ETHWETH-e1c126", - "ContractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "Amount": "725921417141355037005" - }, - { - "DepositNonce": 4661, - "Token": "ETHWSDAI-572803", - "ContractAddress": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", - "Amount": "5431516086574385345409" - }, - { - "DepositNonce": 4662, - "Token": "ETHWDAI-bd65f9", - "ContractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "Amount": "127608014625114134146189" - }, - { - "DepositNonce": 4663, - "Token": "ETHUMB-291202", - "ContractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", - "Amount": "4669367702477323508554075" } ] -} \ No newline at end of file +} From ad8c3fdbdf9d6c1cb33140fd17d4f0a0cd3c01b6 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 6 Sep 2024 13:24:05 +0300 Subject: [PATCH 14/15] - fixes after review --- executors/ethereum/migrationBatchExecutor.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/executors/ethereum/migrationBatchExecutor.go b/executors/ethereum/migrationBatchExecutor.go index 6a2e04ab..adddc767 100644 --- a/executors/ethereum/migrationBatchExecutor.go +++ b/executors/ethereum/migrationBatchExecutor.go @@ -172,8 +172,9 @@ func (executor *migrationBatchExecutor) checkRelayersSigsAndQuorum(relayers []co func (executor *migrationBatchExecutor) getSameMessageHashSignatures() []SignatureInfo { filtered := make([]SignatureInfo, 0, len(executor.signatures)) + expectedMessageHash := executor.batch.MessageHash.String() for _, sigInfo := range executor.signatures { - if sigInfo.MessageHash != executor.batch.MessageHash.String() { + if sigInfo.MessageHash != expectedMessageHash { executor.logger.Warn("found a signature info that was not carried on the same message hash", "local message hash", executor.batch.MessageHash.String(), "address", sigInfo.Address, "message hash", sigInfo.MessageHash) @@ -263,22 +264,22 @@ func (executor *migrationBatchExecutor) checkQuorum(relayers []common.Address, q whitelistedRelayers[relayerAddress] = sigInfo } - result := make([][]byte, 0, len(whitelistedRelayers)) + validSignatures := make([][]byte, 0, len(whitelistedRelayers)) for _, sigInfo := range whitelistedRelayers { sig, err := hex.DecodeString(sigInfo.Signature) if err != nil { return nil, fmt.Errorf("internal error: %w while decoding this string %s that should have been hexed encoded", err, sigInfo.Signature) } - result = append(result, sig) + validSignatures = append(validSignatures, sig) executor.logger.Info("valid signature recorded for whitelisted relayer", "relayer", sigInfo.Address) } - if uint64(len(result)) < quorum.Uint64() { - return nil, fmt.Errorf("%w: minimum %d, got %d", errQuorumNotReached, quorum.Uint64(), len(result)) + if uint64(len(validSignatures)) < quorum.Uint64() { + return nil, fmt.Errorf("%w: minimum %d, got %d", errQuorumNotReached, quorum.Uint64(), len(validSignatures)) } - return result, nil + return validSignatures, nil } func isWhitelistedRelayer(sigInfo SignatureInfo, relayers []common.Address) bool { From 2d7593cb5eae94fca0f788958a25a09e46ab7853 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Fri, 13 Sep 2024 22:23:10 +0300 Subject: [PATCH 15/15] - migration tool fixes --- cmd/migration/config/config.toml | 15 +- cmd/migration/config/migration.json | 80 - cmd/migration/main.go | 15 +- .../bridgeV2Wrappers/contract/Bridge.abi.json | 514 ++++++ .../bridgeV2Wrappers/contract/Bridge.go | 1436 +++++++++++++++++ .../{ => contract}/ERC20Safe.abi.json | 0 .../{ => contract}/ERC20Safe.go | 18 - executors/ethereum/bridgeV2Wrappers/errors.go | 8 + .../bridgeV2Wrappers/ethereumChainWrapper.go | 139 ++ .../ethereumChainWrapper_test.go | 296 ++++ .../ethereum/bridgeV2Wrappers/interface.go | 29 + .../ethereum/bridgeV2Wrappers/mock/errors.go | 7 + .../mock/multiSigContractStub.go | 103 ++ 13 files changed, 2544 insertions(+), 116 deletions(-) delete mode 100644 cmd/migration/config/migration.json create mode 100755 executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json create mode 100755 executors/ethereum/bridgeV2Wrappers/contract/Bridge.go rename executors/ethereum/bridgeV2Wrappers/{ => contract}/ERC20Safe.abi.json (100%) rename executors/ethereum/bridgeV2Wrappers/{ => contract}/ERC20Safe.go (99%) create mode 100644 executors/ethereum/bridgeV2Wrappers/errors.go create mode 100644 executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go create mode 100644 executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go create mode 100644 executors/ethereum/bridgeV2Wrappers/interface.go create mode 100644 executors/ethereum/bridgeV2Wrappers/mock/errors.go create mode 100644 executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml index 801ac522..9958e13c 100644 --- a/cmd/migration/config/config.toml +++ b/cmd/migration/config/config.toml @@ -2,13 +2,14 @@ Chain = "Ethereum" NetworkAddress = "http://127.0.0.1:8545" # a network address PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key - SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + MultisigContractAddress = "92266a070ae1eBA4F778781c2177AaF4747Ea1b8" + SafeContractAddress = "29C63343d302564e3695ca14AB31F5eec427Af3E" GasLimitBase = 350000 GasLimitForEach = 30000 [Eth.GasStation] Enabled = true - URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here - GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + URL = "https://api.bscscan.com/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 5000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 PollingIntervalInSeconds = 60 # number of seconds between gas price polling RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request MaxFetchRetries = 3 # number of fetch retries before printing an error @@ -18,15 +19,15 @@ GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price [MultiversX] - NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address - MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" - SafeContractAddress = "erd1qqqqqqqqqqqqqpgqhxkc48lt5uv2hejj4wtjqvugfm4wgv6gyfkqw0uuxl" + NetworkAddress = "https://testnet-gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqe34pfpl27yq9hq79kms9glwc6c8efm5u3kuq02d609" + SafeContractAddress = "erd1qqqqqqqqqqqqqpgq3quw8f6mplxn6up7l5wsre0dm8r9wrds3kuq7axccv" [MultiversX.Proxy] CacherExpirationSeconds = 600 # the caching time in seconds # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) - RestAPIEntityType = "observer" + RestAPIEntityType = "proxy" FinalityCheck = true MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized diff --git a/cmd/migration/config/migration.json b/cmd/migration/config/migration.json deleted file mode 100644 index 25d106fd..00000000 --- a/cmd/migration/config/migration.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "OldSafeContractAddress": "0x92A26975433A61CF1134802586aa669bAB8B69f3", - "NewSafeContractAddress": "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", - "BatchID": 3548, - "MessageHash": "0x9fad9673d99127cf3bfbcb25fdb271c90009e1199e5ae3bd98f516a6d2b46357", - "DepositsInfo": [ - { - "DepositNonce": 4652, - "Token": "ETHUSDC-220753", - "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "Amount": "7091634513581" - }, - { - "DepositNonce": 4653, - "Token": "ETHUTK-8cdf7a", - "ContractAddress": "0xdc9Ac3C20D1ed0B540dF9b1feDC10039Df13F99c", - "Amount": "224564169411881824066539333" - }, - { - "DepositNonce": 4654, - "Token": "ETHUSDT-9c73c6", - "ContractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "Amount": "318715112062" - }, - { - "DepositNonce": 4655, - "Token": "ETHBUSD-450923", - "ContractAddress": "0x4Fabb145d64652a948d72533023f6E7A623C7C53", - "Amount": "22294352736330153151984" - }, - { - "DepositNonce": 4656, - "Token": "ETHHMT-18538a", - "ContractAddress": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", - "Amount": "435000000000000000000" - }, - { - "DepositNonce": 4657, - "Token": "ETHCGG-ee4e0c", - "ContractAddress": "0x1fE24F25b1Cf609B9c4e7E12D802e3640dFA5e43", - "Amount": "1500138370967581082219795" - }, - { - "DepositNonce": 4658, - "Token": "ETHINFRA-60a3bf", - "ContractAddress": "0x013062189dc3dcc99e9Cee714c513033b8d99e3c", - "Amount": "141172595980399500424091" - }, - { - "DepositNonce": 4659, - "Token": "ETHWBTC-74e282", - "ContractAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "Amount": "3898848955" - }, - { - "DepositNonce": 4660, - "Token": "ETHWETH-e1c126", - "ContractAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "Amount": "725921417141355037005" - }, - { - "DepositNonce": 4661, - "Token": "ETHWSDAI-572803", - "ContractAddress": "0x83F20F44975D03b1b09e64809B757c47f942BEeA", - "Amount": "5431516086574385345409" - }, - { - "DepositNonce": 4662, - "Token": "ETHWDAI-bd65f9", - "ContractAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "Amount": "127608014625114134146189" - }, - { - "DepositNonce": 4663, - "Token": "ETHUMB-291202", - "ContractAddress": "0x6fC13EACE26590B80cCCAB1ba5d51890577D83B2", - "Amount": "4669367702477323508554075" - } - ] -} \ No newline at end of file diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 6dd16854..c4dfb497 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -13,8 +13,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" - "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" - "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/wrappers" "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement" "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement/factory" "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" @@ -22,6 +20,8 @@ import ( "github.com/multiversx/mx-bridge-eth-go/config" "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" chainCore "github.com/multiversx/mx-chain-core-go/core" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-sdk-go/blockchain" @@ -249,19 +249,12 @@ func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } - safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress) - safeInstance, err := contract.NewERC20Safe(safeEthAddress, components.ethClient) - if err != nil { - return err - } - - argsClientWrapper := wrappers.ArgsEthereumChainWrapper{ + argsClientWrapper := bridgeV2Wrappers.ArgsEthereumChainWrapper{ StatusHandler: &disabled.StatusHandler{}, MultiSigContract: multiSigInstance, - SafeContract: safeInstance, BlockchainClient: components.ethClient, } - ethereumChainWrapper, err := wrappers.NewEthereumChainWrapper(argsClientWrapper) + ethereumChainWrapper, err := bridgeV2Wrappers.NewEthereumChainWrapper(argsClientWrapper) if err != nil { return err } diff --git a/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json new file mode 100755 index 00000000..0beb35bd --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json @@ -0,0 +1,514 @@ +[ + { + "inputs": [ + { + "internalType": "address[]", + "name": "board", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "initialQuorum", + "type": "uint256" + }, + { + "internalType": "contract ERC20Safe", + "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": false, + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "name": "QuorumChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "addRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleBlockCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "crossTransferStatuses", + "outputs": [ + { + "internalType": "uint256", + "name": "createdBlockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "depositNonces", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "executeTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "executedBatches", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatchDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRelayer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayersCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + } + ], + "name": "getStatusesAfterExecution", + "outputs": [ + { + "internalType": "enum DepositStatus[]", + "name": "", + "type": "uint8[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isRelayer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "quorum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "removeRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newQuorum", + "type": "uint256" + } + ], + "name": "setQuorum", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + } + ], + "name": "wasBatchExecuted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/executors/ethereum/bridgeV2Wrappers/contract/Bridge.go b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.go new file mode 100755 index 00000000..6c1ad42f --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.go @@ -0,0 +1,1436 @@ +// 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 +) + +// Batch is an auto generated low-level Go binding around an user-defined struct. +type Batch struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +} + +// Deposit is an auto generated low-level Go binding around an user-defined struct. +type Deposit struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +} + +// BridgeMetaData contains all meta data concerning the Bridge contract. +var BridgeMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"board\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"initialQuorum\",\"type\":\"uint256\"},{\"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\":false,\"internalType\":\"uint256\",\"name\":\"quorum\",\"type\":\"uint256\"}],\"name\":\"QuorumChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"addRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleBlockCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"crossTransferStatuses\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"createdBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"depositNonces\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"executeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"executedBatches\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatchDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRelayer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"getStatusesAfterExecution\",\"outputs\":[{\"internalType\":\"enumDepositStatus[]\",\"name\":\"\",\"type\":\"uint8[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"removeRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorum\",\"type\":\"uint256\"}],\"name\":\"setQuorum\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"wasBatchExecuted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// BridgeABI is the input ABI used to generate the binding from. +// Deprecated: Use BridgeMetaData.ABI instead. +var BridgeABI = BridgeMetaData.ABI + +// Bridge is an auto generated Go binding around an Ethereum contract. +type Bridge struct { + BridgeCaller // Read-only binding to the contract + BridgeTransactor // Write-only binding to the contract + BridgeFilterer // Log filterer for contract events +} + +// BridgeCaller is an auto generated read-only Go binding around an Ethereum contract. +type BridgeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BridgeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BridgeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BridgeSession struct { + Contract *Bridge // 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 +} + +// BridgeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BridgeCallerSession struct { + Contract *BridgeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BridgeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BridgeTransactorSession struct { + Contract *BridgeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BridgeRaw is an auto generated low-level Go binding around an Ethereum contract. +type BridgeRaw struct { + Contract *Bridge // Generic contract binding to access the raw methods on +} + +// BridgeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BridgeCallerRaw struct { + Contract *BridgeCaller // Generic read-only contract binding to access the raw methods on +} + +// BridgeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BridgeTransactorRaw struct { + Contract *BridgeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBridge creates a new instance of Bridge, bound to a specific deployed contract. +func NewBridge(address common.Address, backend bind.ContractBackend) (*Bridge, error) { + contract, err := bindBridge(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Bridge{BridgeCaller: BridgeCaller{contract: contract}, BridgeTransactor: BridgeTransactor{contract: contract}, BridgeFilterer: BridgeFilterer{contract: contract}}, nil +} + +// NewBridgeCaller creates a new read-only instance of Bridge, bound to a specific deployed contract. +func NewBridgeCaller(address common.Address, caller bind.ContractCaller) (*BridgeCaller, error) { + contract, err := bindBridge(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BridgeCaller{contract: contract}, nil +} + +// NewBridgeTransactor creates a new write-only instance of Bridge, bound to a specific deployed contract. +func NewBridgeTransactor(address common.Address, transactor bind.ContractTransactor) (*BridgeTransactor, error) { + contract, err := bindBridge(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BridgeTransactor{contract: contract}, nil +} + +// NewBridgeFilterer creates a new log filterer instance of Bridge, bound to a specific deployed contract. +func NewBridgeFilterer(address common.Address, filterer bind.ContractFilterer) (*BridgeFilterer, error) { + contract, err := bindBridge(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BridgeFilterer{contract: contract}, nil +} + +// bindBridge binds a generic wrapper to an already deployed contract. +func bindBridge(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BridgeMetaData.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 (_Bridge *BridgeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bridge.Contract.BridgeCaller.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 (_Bridge *BridgeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.Contract.BridgeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bridge *BridgeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bridge.Contract.BridgeTransactor.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 (_Bridge *BridgeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bridge.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 (_Bridge *BridgeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bridge *BridgeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bridge.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 (_Bridge *BridgeCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Bridge.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 (_Bridge *BridgeSession) Admin() (common.Address, error) { + return _Bridge.Contract.Admin(&_Bridge.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Bridge *BridgeCallerSession) Admin() (common.Address, error) { + return _Bridge.Contract.Admin(&_Bridge.CallOpts) +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeCaller) BatchSettleBlockCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "batchSettleBlockCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeSession) BatchSettleBlockCount() (*big.Int, error) { + return _Bridge.Contract.BatchSettleBlockCount(&_Bridge.CallOpts) +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeCallerSession) BatchSettleBlockCount() (*big.Int, error) { + return _Bridge.Contract.BatchSettleBlockCount(&_Bridge.CallOpts) +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeCaller) CrossTransferStatuses(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "crossTransferStatuses", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeSession) CrossTransferStatuses(arg0 *big.Int) (*big.Int, error) { + return _Bridge.Contract.CrossTransferStatuses(&_Bridge.CallOpts, arg0) +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeCallerSession) CrossTransferStatuses(arg0 *big.Int) (*big.Int, error) { + return _Bridge.Contract.CrossTransferStatuses(&_Bridge.CallOpts, arg0) +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeCaller) ExecutedBatches(opts *bind.CallOpts, arg0 *big.Int) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "executedBatches", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeSession) ExecutedBatches(arg0 *big.Int) (bool, error) { + return _Bridge.Contract.ExecutedBatches(&_Bridge.CallOpts, arg0) +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeCallerSession) ExecutedBatches(arg0 *big.Int) (bool, error) { + return _Bridge.Contract.ExecutedBatches(&_Bridge.CallOpts, arg0) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_Bridge *BridgeCaller) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (Batch, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getBatch", batchNonce) + + if err != nil { + return *new(Batch), err + } + + out0 := *abi.ConvertType(out[0], new(Batch)).(*Batch) + + return out0, err + +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_Bridge *BridgeSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _Bridge.Contract.GetBatch(&_Bridge.CallOpts, batchNonce) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_Bridge *BridgeCallerSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _Bridge.Contract.GetBatch(&_Bridge.CallOpts, batchNonce) +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_Bridge *BridgeCaller) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]Deposit, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getBatchDeposits", batchNonce) + + if err != nil { + return *new([]Deposit), err + } + + out0 := *abi.ConvertType(out[0], new([]Deposit)).(*[]Deposit) + + return out0, err + +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_Bridge *BridgeSession) GetBatchDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _Bridge.Contract.GetBatchDeposits(&_Bridge.CallOpts, batchNonce) +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_Bridge *BridgeCallerSession) GetBatchDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _Bridge.Contract.GetBatchDeposits(&_Bridge.CallOpts, batchNonce) +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeCaller) GetRelayer(opts *bind.CallOpts, index *big.Int) (common.Address, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayer", index) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeSession) GetRelayer(index *big.Int) (common.Address, error) { + return _Bridge.Contract.GetRelayer(&_Bridge.CallOpts, index) +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeCallerSession) GetRelayer(index *big.Int) (common.Address, error) { + return _Bridge.Contract.GetRelayer(&_Bridge.CallOpts, index) +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeCaller) GetRelayers(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayers") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeSession) GetRelayers() ([]common.Address, error) { + return _Bridge.Contract.GetRelayers(&_Bridge.CallOpts) +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeCallerSession) GetRelayers() ([]common.Address, error) { + return _Bridge.Contract.GetRelayers(&_Bridge.CallOpts) +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeCaller) GetRelayersCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayersCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeSession) GetRelayersCount() (*big.Int, error) { + return _Bridge.Contract.GetRelayersCount(&_Bridge.CallOpts) +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeCallerSession) GetRelayersCount() (*big.Int, error) { + return _Bridge.Contract.GetRelayersCount(&_Bridge.CallOpts) +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceElrondETH) view returns(uint8[]) +func (_Bridge *BridgeCaller) GetStatusesAfterExecution(opts *bind.CallOpts, batchNonceElrondETH *big.Int) ([]uint8, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getStatusesAfterExecution", batchNonceElrondETH) + + if err != nil { + return *new([]uint8), err + } + + out0 := *abi.ConvertType(out[0], new([]uint8)).(*[]uint8) + + return out0, err + +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceElrondETH) view returns(uint8[]) +func (_Bridge *BridgeSession) GetStatusesAfterExecution(batchNonceElrondETH *big.Int) ([]uint8, error) { + return _Bridge.Contract.GetStatusesAfterExecution(&_Bridge.CallOpts, batchNonceElrondETH) +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceElrondETH) view returns(uint8[]) +func (_Bridge *BridgeCallerSession) GetStatusesAfterExecution(batchNonceElrondETH *big.Int) ([]uint8, error) { + return _Bridge.Contract.GetStatusesAfterExecution(&_Bridge.CallOpts, batchNonceElrondETH) +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeCaller) IsRelayer(opts *bind.CallOpts, account common.Address) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "isRelayer", account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeSession) IsRelayer(account common.Address) (bool, error) { + return _Bridge.Contract.IsRelayer(&_Bridge.CallOpts, account) +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeCallerSession) IsRelayer(account common.Address) (bool, error) { + return _Bridge.Contract.IsRelayer(&_Bridge.CallOpts, account) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeCaller) Paused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "paused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeSession) Paused() (bool, error) { + return _Bridge.Contract.Paused(&_Bridge.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeCallerSession) Paused() (bool, error) { + return _Bridge.Contract.Paused(&_Bridge.CallOpts) +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeCaller) Quorum(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "quorum") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeSession) Quorum() (*big.Int, error) { + return _Bridge.Contract.Quorum(&_Bridge.CallOpts) +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeCallerSession) Quorum() (*big.Int, error) { + return _Bridge.Contract.Quorum(&_Bridge.CallOpts) +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceElrondETH) view returns(bool) +func (_Bridge *BridgeCaller) WasBatchExecuted(opts *bind.CallOpts, batchNonceElrondETH *big.Int) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "wasBatchExecuted", batchNonceElrondETH) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceElrondETH) view returns(bool) +func (_Bridge *BridgeSession) WasBatchExecuted(batchNonceElrondETH *big.Int) (bool, error) { + return _Bridge.Contract.WasBatchExecuted(&_Bridge.CallOpts, batchNonceElrondETH) +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceElrondETH) view returns(bool) +func (_Bridge *BridgeCallerSession) WasBatchExecuted(batchNonceElrondETH *big.Int) (bool, error) { + return _Bridge.Contract.WasBatchExecuted(&_Bridge.CallOpts, batchNonceElrondETH) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeTransactor) AddRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "addRelayer", account) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeSession) AddRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.AddRelayer(&_Bridge.TransactOpts, account) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) AddRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.AddRelayer(&_Bridge.TransactOpts, account) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceElrondETH, bytes[] signatures) returns() +func (_Bridge *BridgeTransactor) ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceElrondETH *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "executeTransfer", tokens, recipients, amounts, depositNonces, batchNonceElrondETH, signatures) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceElrondETH, bytes[] signatures) returns() +func (_Bridge *BridgeSession) ExecuteTransfer(tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceElrondETH *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.Contract.ExecuteTransfer(&_Bridge.TransactOpts, tokens, recipients, amounts, depositNonces, batchNonceElrondETH, signatures) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceElrondETH, bytes[] signatures) returns() +func (_Bridge *BridgeTransactorSession) ExecuteTransfer(tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceElrondETH *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.Contract.ExecuteTransfer(&_Bridge.TransactOpts, tokens, recipients, amounts, depositNonces, batchNonceElrondETH, signatures) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "pause") +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeSession) Pause() (*types.Transaction, error) { + return _Bridge.Contract.Pause(&_Bridge.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeTransactorSession) Pause() (*types.Transaction, error) { + return _Bridge.Contract.Pause(&_Bridge.TransactOpts) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeTransactor) RemoveRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "removeRelayer", account) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeSession) RemoveRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RemoveRelayer(&_Bridge.TransactOpts, account) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) RemoveRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RemoveRelayer(&_Bridge.TransactOpts, account) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeSession) RenounceAdmin() (*types.Transaction, error) { + return _Bridge.Contract.RenounceAdmin(&_Bridge.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _Bridge.Contract.RenounceAdmin(&_Bridge.TransactOpts) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeTransactor) RenounceRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "renounceRelayer", account) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeSession) RenounceRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RenounceRelayer(&_Bridge.TransactOpts, account) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) RenounceRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RenounceRelayer(&_Bridge.TransactOpts, account) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeTransactor) SetQuorum(opts *bind.TransactOpts, newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "setQuorum", newQuorum) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeSession) SetQuorum(newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.Contract.SetQuorum(&_Bridge.TransactOpts, newQuorum) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeTransactorSession) SetQuorum(newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.Contract.SetQuorum(&_Bridge.TransactOpts, newQuorum) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.Contract.TransferAdmin(&_Bridge.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.Contract.TransferAdmin(&_Bridge.TransactOpts, newAdmin) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "unpause") +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeSession) Unpause() (*types.Transaction, error) { + return _Bridge.Contract.Unpause(&_Bridge.TransactOpts) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeTransactorSession) Unpause() (*types.Transaction, error) { + return _Bridge.Contract.Unpause(&_Bridge.TransactOpts) +} + +// BridgeAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the Bridge contract. +type BridgeAdminRoleTransferredIterator struct { + Event *BridgeAdminRoleTransferred // 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 *BridgeAdminRoleTransferredIterator) 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(BridgeAdminRoleTransferred) + 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(BridgeAdminRoleTransferred) + 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 *BridgeAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeAdminRoleTransferred represents a AdminRoleTransferred event raised by the Bridge contract. +type BridgeAdminRoleTransferred 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 (_Bridge *BridgeFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*BridgeAdminRoleTransferredIterator, 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 := _Bridge.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &BridgeAdminRoleTransferredIterator{contract: _Bridge.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 (_Bridge *BridgeFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *BridgeAdminRoleTransferred, 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 := _Bridge.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(BridgeAdminRoleTransferred) + if err := _Bridge.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 (_Bridge *BridgeFilterer) ParseAdminRoleTransferred(log types.Log) (*BridgeAdminRoleTransferred, error) { + event := new(BridgeAdminRoleTransferred) + if err := _Bridge.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeQuorumChangedIterator is returned from FilterQuorumChanged and is used to iterate over the raw logs and unpacked data for QuorumChanged events raised by the Bridge contract. +type BridgeQuorumChangedIterator struct { + Event *BridgeQuorumChanged // 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 *BridgeQuorumChangedIterator) 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(BridgeQuorumChanged) + 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(BridgeQuorumChanged) + 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 *BridgeQuorumChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeQuorumChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeQuorumChanged represents a QuorumChanged event raised by the Bridge contract. +type BridgeQuorumChanged struct { + Quorum *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterQuorumChanged is a free log retrieval operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) FilterQuorumChanged(opts *bind.FilterOpts) (*BridgeQuorumChangedIterator, error) { + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "QuorumChanged") + if err != nil { + return nil, err + } + return &BridgeQuorumChangedIterator{contract: _Bridge.contract, event: "QuorumChanged", logs: logs, sub: sub}, nil +} + +// WatchQuorumChanged is a free log subscription operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) WatchQuorumChanged(opts *bind.WatchOpts, sink chan<- *BridgeQuorumChanged) (event.Subscription, error) { + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "QuorumChanged") + 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(BridgeQuorumChanged) + if err := _Bridge.contract.UnpackLog(event, "QuorumChanged", 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 +} + +// ParseQuorumChanged is a log parse operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) ParseQuorumChanged(log types.Log) (*BridgeQuorumChanged, error) { + event := new(BridgeQuorumChanged) + if err := _Bridge.contract.UnpackLog(event, "QuorumChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeRelayerAddedIterator is returned from FilterRelayerAdded and is used to iterate over the raw logs and unpacked data for RelayerAdded events raised by the Bridge contract. +type BridgeRelayerAddedIterator struct { + Event *BridgeRelayerAdded // 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 *BridgeRelayerAddedIterator) 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(BridgeRelayerAdded) + 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(BridgeRelayerAdded) + 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 *BridgeRelayerAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeRelayerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeRelayerAdded represents a RelayerAdded event raised by the Bridge contract. +type BridgeRelayerAdded struct { + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayerAdded is a free log retrieval operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) FilterRelayerAdded(opts *bind.FilterOpts, account []common.Address, sender []common.Address) (*BridgeRelayerAddedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "RelayerAdded", accountRule, senderRule) + if err != nil { + return nil, err + } + return &BridgeRelayerAddedIterator{contract: _Bridge.contract, event: "RelayerAdded", logs: logs, sub: sub}, nil +} + +// WatchRelayerAdded is a free log subscription operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) WatchRelayerAdded(opts *bind.WatchOpts, sink chan<- *BridgeRelayerAdded, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "RelayerAdded", accountRule, senderRule) + 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(BridgeRelayerAdded) + if err := _Bridge.contract.UnpackLog(event, "RelayerAdded", 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 +} + +// ParseRelayerAdded is a log parse operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) ParseRelayerAdded(log types.Log) (*BridgeRelayerAdded, error) { + event := new(BridgeRelayerAdded) + if err := _Bridge.contract.UnpackLog(event, "RelayerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeRelayerRemovedIterator is returned from FilterRelayerRemoved and is used to iterate over the raw logs and unpacked data for RelayerRemoved events raised by the Bridge contract. +type BridgeRelayerRemovedIterator struct { + Event *BridgeRelayerRemoved // 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 *BridgeRelayerRemovedIterator) 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(BridgeRelayerRemoved) + 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(BridgeRelayerRemoved) + 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 *BridgeRelayerRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeRelayerRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeRelayerRemoved represents a RelayerRemoved event raised by the Bridge contract. +type BridgeRelayerRemoved struct { + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayerRemoved is a free log retrieval operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) FilterRelayerRemoved(opts *bind.FilterOpts, account []common.Address, sender []common.Address) (*BridgeRelayerRemovedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "RelayerRemoved", accountRule, senderRule) + if err != nil { + return nil, err + } + return &BridgeRelayerRemovedIterator{contract: _Bridge.contract, event: "RelayerRemoved", logs: logs, sub: sub}, nil +} + +// WatchRelayerRemoved is a free log subscription operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) WatchRelayerRemoved(opts *bind.WatchOpts, sink chan<- *BridgeRelayerRemoved, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "RelayerRemoved", accountRule, senderRule) + 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(BridgeRelayerRemoved) + if err := _Bridge.contract.UnpackLog(event, "RelayerRemoved", 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 +} + +// ParseRelayerRemoved is a log parse operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) ParseRelayerRemoved(log types.Log) (*BridgeRelayerRemoved, error) { + event := new(BridgeRelayerRemoved) + if err := _Bridge.contract.UnpackLog(event, "RelayerRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.abi.json similarity index 100% rename from executors/ethereum/bridgeV2Wrappers/ERC20Safe.abi.json rename to executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.abi.json diff --git a/executors/ethereum/bridgeV2Wrappers/ERC20Safe.go b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go similarity index 99% rename from executors/ethereum/bridgeV2Wrappers/ERC20Safe.go rename to executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go index 548d35f1..e397ab67 100644 --- a/executors/ethereum/bridgeV2Wrappers/ERC20Safe.go +++ b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go @@ -29,24 +29,6 @@ var ( _ = abi.ConvertType ) -// Batch is an auto generated low-level Go binding around an user-defined struct. -type Batch struct { - Nonce *big.Int - BlockNumber uint64 - LastUpdatedBlockNumber uint64 - DepositsCount uint16 -} - -// Deposit is an auto generated low-level Go binding around an user-defined struct. -type Deposit struct { - Nonce *big.Int - TokenAddress common.Address - Amount *big.Int - Depositor common.Address - Recipient [32]byte - Status uint8 -} - // ERC20SafeMetaData contains all meta data concerning the ERC20Safe contract. var ERC20SafeMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"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\":\"address\",\"name\":\"previousBridge\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"BridgeTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"depositNonce\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"batchId\",\"type\":\"uint112\"}],\"name\":\"ERC20Deposit\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchBlockLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batchDeposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSize\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batches\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchesCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridge\",\"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\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositsCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMaxLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMinLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initSupply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isAnyBatchInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isTokenWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"recoverLostFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeTokenFromWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchBlockLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchBlockLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newBatchSize\",\"type\":\"uint16\"}],\"name\":\"setBatchSize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"setBridge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMaxLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMinLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMaxLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMinLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minimumAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maximumAmount\",\"type\":\"uint256\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistedTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", diff --git a/executors/ethereum/bridgeV2Wrappers/errors.go b/executors/ethereum/bridgeV2Wrappers/errors.go new file mode 100644 index 00000000..578f626f --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/errors.go @@ -0,0 +1,8 @@ +package bridgeV2Wrappers + +import "errors" + +var ( + errNilBlockchainClient = errors.New("nil blockchain client") + errNilMultiSigContract = errors.New("nil multi sig contract") +) diff --git a/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go new file mode 100644 index 00000000..2ac16fe7 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go @@ -0,0 +1,139 @@ +package bridgeV2Wrappers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" + "github.com/multiversx/mx-chain-core-go/core/check" +) + +// ArgsEthereumChainWrapper is the DTO used to construct a ethereumChainWrapper instance +type ArgsEthereumChainWrapper struct { + StatusHandler core.StatusHandler + MultiSigContract multiSigContract + BlockchainClient blockchainClient +} + +type ethereumChainWrapper struct { + core.StatusHandler + multiSigContract multiSigContract + blockchainClient blockchainClient +} + +// NewEthereumChainWrapper creates a new instance of type ethereumChainWrapper +func NewEthereumChainWrapper(args ArgsEthereumChainWrapper) (*ethereumChainWrapper, error) { + err := checkArgs(args) + if err != nil { + return nil, err + } + + return ðereumChainWrapper{ + StatusHandler: args.StatusHandler, + multiSigContract: args.MultiSigContract, + blockchainClient: args.BlockchainClient, + }, nil +} + +func checkArgs(args ArgsEthereumChainWrapper) error { + if check.IfNil(args.StatusHandler) { + return clients.ErrNilStatusHandler + } + if check.IfNilReflect(args.MultiSigContract) { + return errNilMultiSigContract + } + if check.IfNilReflect(args.BlockchainClient) { + return errNilBlockchainClient + } + + return nil +} + +// GetBatch returns the batch of transactions by providing the batch nonce +func (wrapper *ethereumChainWrapper) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetBatch(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// GetBatchDeposits returns the transactions of a batch by providing the batch nonce +func (wrapper *ethereumChainWrapper) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetBatchDeposits(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// GetRelayers returns all whitelisted ethereum addresses +func (wrapper *ethereumChainWrapper) GetRelayers(ctx context.Context) ([]common.Address, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetRelayers(&bind.CallOpts{Context: ctx}) +} + +// WasBatchExecuted returns true if the batch was executed +func (wrapper *ethereumChainWrapper) WasBatchExecuted(ctx context.Context, batchNonce *big.Int) (bool, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.WasBatchExecuted(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// ChainID returns the chain ID +func (wrapper *ethereumChainWrapper) ChainID(ctx context.Context) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.ChainID(ctx) +} + +// BlockNumber returns the current ethereum block number +func (wrapper *ethereumChainWrapper) BlockNumber(ctx context.Context) (uint64, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + val, err := wrapper.blockchainClient.BlockNumber(ctx) + if err != nil { + return 0, err + } + + wrapper.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, int(val)) + + return val, nil +} + +// NonceAt returns the account's nonce at the specified block number +func (wrapper *ethereumChainWrapper) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.NonceAt(ctx, account, blockNumber) +} + +// ExecuteTransfer will send an execute-transfer transaction on the ethereum chain +func (wrapper *ethereumChainWrapper) ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + wrapper.AddIntMetric(core.MetricNumEthClientTransactions, 1) + return wrapper.multiSigContract.ExecuteTransfer(opts, tokens, recipients, amounts, nonces, batchNonce, signatures) +} + +// Quorum returns the current set quorum value +func (wrapper *ethereumChainWrapper) Quorum(ctx context.Context) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.Quorum(&bind.CallOpts{Context: ctx}) +} + +// GetStatusesAfterExecution returns the statuses of the last executed transfer +func (wrapper *ethereumChainWrapper) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetStatusesAfterExecution(&bind.CallOpts{Context: ctx}, batchID) +} + +// BalanceAt returns the wei balance of the given account. +// The block number can be nil, in which case the balance is taken from the latest known block. +func (wrapper *ethereumChainWrapper) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.BalanceAt(ctx, account, blockNumber) +} + +// IsPaused returns true if the multisig contract is paused +func (wrapper *ethereumChainWrapper) IsPaused(ctx context.Context) (bool, error) { + return wrapper.multiSigContract.Paused(&bind.CallOpts{Context: ctx}) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (wrapper *ethereumChainWrapper) IsInterfaceNil() bool { + return wrapper == nil +} diff --git a/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go new file mode 100644 index 00000000..6540877b --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go @@ -0,0 +1,296 @@ +package bridgeV2Wrappers + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/mock" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/interactors" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/stretchr/testify/assert" +) + +func createMockArgsEthereumChainWrapper() (ArgsEthereumChainWrapper, *testsCommon.StatusHandlerMock) { + statusHandler := testsCommon.NewStatusHandlerMock("mock") + + return ArgsEthereumChainWrapper{ + MultiSigContract: &mock.MultiSigContractStub{}, + BlockchainClient: &interactors.BlockchainClientStub{}, + StatusHandler: statusHandler, + }, statusHandler +} + +func TestNewMultiSigContractWrapper(t *testing.T) { + t.Parallel() + + t.Run("nil status handler", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.StatusHandler = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, clients.ErrNilStatusHandler, err) + }) + t.Run("nil blockchain client", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.BlockchainClient = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, errNilBlockchainClient, err) + }) + t.Run("nil multisig 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() + + args, _ := createMockArgsEthereumChainWrapper() + + wrapper, err := NewEthereumChainWrapper(args) + assert.False(t, check.IfNil(wrapper)) + assert.Nil(t, err) + }) +} + +func TestEthClientWrapper_GetBatch(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + providedBatchID := big.NewInt(223) + args.MultiSigContract = &mock.MultiSigContractStub{ + GetBatchCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { + handlerCalled = true + assert.Equal(t, providedBatchID, batchNonce) + return contract.Batch{}, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + batch, err := wrapper.GetBatch(context.Background(), providedBatchID) + assert.Nil(t, err) + assert.Equal(t, contract.Batch{}, batch) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_GetRelayers(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + GetRelayersCalled: func(opts *bind.CallOpts) ([]common.Address, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + relayers, err := wrapper.GetRelayers(context.Background()) + assert.Nil(t, err) + assert.Nil(t, relayers) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_WasBatchExecuted(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + WasBatchExecutedCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) { + handlerCalled = true + return false, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + executed, err := wrapper.WasBatchExecuted(context.Background(), nil) + assert.Nil(t, err) + assert.False(t, executed) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_ChainID(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.BlockchainClient = &interactors.BlockchainClientStub{ + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + chainID, err := wrapper.ChainID(context.Background()) + assert.Nil(t, err) + assert.Nil(t, chainID) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_BlockNumber(t *testing.T) { + t.Parallel() + + t.Run("block number call returns error", func(t *testing.T) { + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + expectedError := errors.New("expected error") + args.BlockchainClient = &interactors.BlockchainClientStub{ + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + handlerCalled = true + return 0, expectedError + }, + } + lastBlockNum := 3343 + statusHandler.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, lastBlockNum) + + wrapper, _ := NewEthereumChainWrapper(args) + blockNum, err := wrapper.BlockNumber(context.Background()) + assert.Equal(t, expectedError, err) + assert.Equal(t, uint64(0), blockNum) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) + assert.Equal(t, lastBlockNum, statusHandler.GetIntMetric(core.MetricLastQueriedEthereumBlockNumber)) + }) + t.Run("block number call returns a value", func(t *testing.T) { + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + newBlockNum := 772537 + args.BlockchainClient = &interactors.BlockchainClientStub{ + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + handlerCalled = true + return uint64(newBlockNum), nil + }, + } + lastBlockNum := 3343 + statusHandler.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, lastBlockNum) + + wrapper, _ := NewEthereumChainWrapper(args) + blockNum, err := wrapper.BlockNumber(context.Background()) + assert.Nil(t, err) + assert.Equal(t, uint64(newBlockNum), blockNum) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) + assert.Equal(t, newBlockNum, statusHandler.GetIntMetric(core.MetricLastQueriedEthereumBlockNumber)) + }) +} + +func TestEthClientWrapper_NonceAt(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.BlockchainClient = &interactors.BlockchainClientStub{ + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + handlerCalled = true + return 0, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + nonce, err := wrapper.NonceAt(context.Background(), common.Address{}, nil) + assert.Nil(t, err) + assert.Equal(t, uint64(0), nonce) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_ExecuteTransfer(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, + amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + tx, err := wrapper.ExecuteTransfer(nil, nil, nil, nil, nil, nil, nil) + assert.Nil(t, err) + assert.Nil(t, tx) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientTransactions)) +} + +func TestEthClientWrapper_Quorum(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + QuorumCalled: func(opts *bind.CallOpts) (*big.Int, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + tx, err := wrapper.Quorum(context.Background()) + assert.Nil(t, err) + assert.Nil(t, tx) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_GetStatusesAfterExecution(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + GetStatusesAfterExecutionCalled: func(opts *bind.CallOpts, batchNonceMultiversXETH *big.Int) ([]uint8, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + statuses, err := wrapper.GetStatusesAfterExecution(context.Background(), nil) + assert.Nil(t, err) + assert.Nil(t, statuses) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthereumChainWrapper_IsPaused(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + PausedCalled: func(opts *bind.CallOpts) (bool, error) { + handlerCalled = true + return true, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + result, err := wrapper.IsPaused(context.Background()) + + assert.Nil(t, err) + assert.True(t, result) + assert.True(t, handlerCalled) +} diff --git a/executors/ethereum/bridgeV2Wrappers/interface.go b/executors/ethereum/bridgeV2Wrappers/interface.go new file mode 100644 index 00000000..2d4644c2 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/interface.go @@ -0,0 +1,29 @@ +package bridgeV2Wrappers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" +) + +type multiSigContract interface { + GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) + GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetRelayers(opts *bind.CallOpts) ([]common.Address, error) + WasBatchExecuted(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) + ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) + Quorum(opts *bind.CallOpts) (*big.Int, error) + GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + Paused(opts *bind.CallOpts) (bool, error) +} + +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) +} diff --git a/executors/ethereum/bridgeV2Wrappers/mock/errors.go b/executors/ethereum/bridgeV2Wrappers/mock/errors.go new file mode 100644 index 00000000..9386b84f --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/mock/errors.go @@ -0,0 +1,7 @@ +package mock + +import "errors" + +var ( + errNotImplemented = errors.New("method not implemented") +) diff --git a/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go b/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go new file mode 100644 index 00000000..0750d06d --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go @@ -0,0 +1,103 @@ +package mock + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" +) + +// MultiSigContractStub - +type MultiSigContractStub struct { + GetBatchCalled func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) + GetBatchDepositsCalled func(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetRelayersCalled func(opts *bind.CallOpts) ([]common.Address, error) + WasBatchExecutedCalled func(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) + ExecuteTransferCalled func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, + amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) + QuorumCalled func(opts *bind.CallOpts) (*big.Int, error) + GetStatusesAfterExecutionCalled func(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + PausedCalled func(opts *bind.CallOpts) (bool, error) +} + +// GetBatch - +func (stub *MultiSigContractStub) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { + if stub.GetBatchCalled != nil { + return stub.GetBatchCalled(opts, batchNonce) + } + + return contract.Batch{}, nil +} + +// GetBatchDeposits - +func (stub *MultiSigContractStub) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) { + if stub.GetBatchCalled != nil { + return stub.GetBatchDepositsCalled(opts, batchNonce) + } + + return make([]contract.Deposit, 0), nil +} + +// GetRelayers - +func (stub *MultiSigContractStub) GetRelayers(opts *bind.CallOpts) ([]common.Address, error) { + if stub.GetRelayersCalled != nil { + return stub.GetRelayersCalled(opts) + } + + return make([]common.Address, 0), nil +} + +// WasBatchExecuted - +func (stub *MultiSigContractStub) WasBatchExecuted(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) { + if stub.WasBatchExecutedCalled != nil { + return stub.WasBatchExecutedCalled(opts, batchNonce) + } + + return false, nil +} + +// ExecuteTransfer - +func (stub *MultiSigContractStub) ExecuteTransfer( + opts *bind.TransactOpts, + tokens []common.Address, + recipients []common.Address, + amounts []*big.Int, + nonces []*big.Int, + batchNonce *big.Int, + signatures [][]byte, +) (*types.Transaction, error) { + if stub.ExecuteTransferCalled != nil { + return stub.ExecuteTransferCalled(opts, tokens, recipients, amounts, nonces, batchNonce, signatures) + } + + return nil, errNotImplemented +} + +// Quorum - +func (stub *MultiSigContractStub) Quorum(opts *bind.CallOpts) (*big.Int, error) { + if stub.QuorumCalled != nil { + return stub.QuorumCalled(opts) + } + + return big.NewInt(0), nil +} + +// GetStatusesAfterExecution - +func (stub *MultiSigContractStub) GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) { + if stub.GetStatusesAfterExecutionCalled != nil { + return stub.GetStatusesAfterExecutionCalled(opts, batchID) + } + + return make([]byte, 0), nil +} + +// Paused - +func (stub *MultiSigContractStub) Paused(opts *bind.CallOpts) (bool, error) { + if stub.PausedCalled != nil { + return stub.PausedCalled(opts) + } + + return false, nil +}