From 40318f9be6a10f7fb2975a36ed274a09f5a233e9 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Thu, 14 Nov 2024 13:38:22 +0200 Subject: [PATCH 1/2] - added support for all token balance - fixed a bug when fetching the gas price - adjusted the gas limit on ethereum --- cmd/migration/config/config-mainnet-eth.toml | 2 +- cmd/migration/interface.go | 3 +- cmd/migration/main.go | 29 +++++++++ executors/ethereum/common.go | 47 ++++++++++++-- executors/ethereum/common_test.go | 28 +++++++-- executors/ethereum/migrationBatchCreator.go | 15 ++--- .../ethereum/migrationBatchCreator_test.go | 62 +++++++++++++++++-- 7 files changed, 162 insertions(+), 24 deletions(-) diff --git a/cmd/migration/config/config-mainnet-eth.toml b/cmd/migration/config/config-mainnet-eth.toml index a1a4d8ff..19fd556d 100644 --- a/cmd/migration/config/config-mainnet-eth.toml +++ b/cmd/migration/config/config-mainnet-eth.toml @@ -4,7 +4,7 @@ PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key MultisigContractAddress = "0x1Ff78EB04d44a803E73c44FEf8790c5cAbD14596" SafeContractAddress = "0x92A26975433A61CF1134802586aa669bAB8B69f3" - GasLimitBase = 350000 + GasLimitBase = 400000 GasLimitForEach = 30000 [Eth.GasStation] Enabled = true diff --git a/cmd/migration/interface.go b/cmd/migration/interface.go index 086ccffc..7d71c7fc 100644 --- a/cmd/migration/interface.go +++ b/cmd/migration/interface.go @@ -2,7 +2,6 @@ package main import ( "context" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" @@ -11,5 +10,5 @@ import ( // BatchCreator defines the operations implemented by an entity that can create an Ethereum batch message that can be used // in signing or transfer execution type BatchCreator interface { - CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*big.Float) (*ethereum.BatchInfo, error) + CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*ethereum.FloatWrapper) (*ethereum.BatchInfo, error) } diff --git a/cmd/migration/main.go b/cmd/migration/main.go index 5ff13500..bb60a9cd 100644 --- a/cmd/migration/main.go +++ b/cmd/migration/main.go @@ -6,12 +6,14 @@ import ( "encoding/hex" "encoding/json" "fmt" + "math/big" "os" "strings" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/multiversx/mx-bridge-eth-go/clients" ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement" "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement/factory" @@ -309,6 +311,11 @@ func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error { return err } + err = waitForGasPrice(gs) + if err != nil { + return err + } + args := ethereum.ArgsMigrationBatchExecutor{ EthereumChainWrapper: components.ethereumChainWrapper, CryptoHandler: components.cryptoHandler, @@ -328,6 +335,28 @@ func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error { return executor.ExecuteTransfer(context.Background()) } +func waitForGasPrice(gs clients.GasHandler) error { + log.Info("Fetching a gas price value. Please wait...") + numRetries := 5 + timeBetweenChecks := time.Second + + var err error + var gasPrice *big.Int + for i := 0; i < numRetries; i++ { + time.Sleep(timeBetweenChecks) + gasPrice, err = gs.GetCurrentGasPrice() + if err != nil { + log.Debug("waitForGasPrice", "error", err) + continue + } + + log.Info("Fetched the gas price", "value", gasPrice.String()) + return nil + } + + return err +} + func loadConfig(filepath string) (config.MigrationToolConfig, error) { cfg := config.MigrationToolConfig{} err := chainCore.LoadTomlFile(&cfg, filepath) diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go index c4a93cdb..def84b8e 100644 --- a/executors/ethereum/common.go +++ b/executors/ethereum/common.go @@ -37,6 +37,14 @@ type SignatureInfo struct { Signature string `json:"Signature"` } +// FloatWrapper is a wrapper of the big.Float that supports specifying if the value is maximum +type FloatWrapper struct { + *big.Float + IsMax bool +} + +var maxValues = []string{"all", "max", "*"} + // TokensBalancesDisplayString will convert the deposit balances into a human-readable string func TokensBalancesDisplayString(batchInfo *BatchInfo) string { maxTokenLen := 0 @@ -69,12 +77,12 @@ func TokensBalancesDisplayString(batchInfo *BatchInfo) string { } // ConvertPartialMigrationStringToMap converts the partial migration string to its map representation -func ConvertPartialMigrationStringToMap(partialMigration string) (map[string]*big.Float, error) { +func ConvertPartialMigrationStringToMap(partialMigration string) (map[string]*FloatWrapper, error) { partsSeparator := "," tokenAmountSeparator := ":" parts := strings.Split(partialMigration, partsSeparator) - partialMap := make(map[string]*big.Float) + partialMap := make(map[string]*FloatWrapper) for _, part := range parts { part = strings.Trim(part, " \t\n") splt := strings.Split(part, tokenAmountSeparator) @@ -82,19 +90,48 @@ func ConvertPartialMigrationStringToMap(partialMigration string) (map[string]*bi return nil, fmt.Errorf("%w at token %s, invalid format", errInvalidPartialMigrationString, part) } + token := splt[0] + if isMaxValueString(splt[1]) { + partialMap[token] = &FloatWrapper{ + Float: big.NewFloat(0), + IsMax: true, + } + + continue + } + amount, ok := big.NewFloat(0).SetString(splt[1]) if !ok { return nil, fmt.Errorf("%w at token %s, not a number", errInvalidPartialMigrationString, part) } - token := splt[0] if partialMap[token] == nil { - partialMap[token] = big.NewFloat(0).Set(amount) + partialMap[token] = &FloatWrapper{ + Float: big.NewFloat(0).Set(amount), + IsMax: false, + } continue } - partialMap[token].Add(partialMap[token], amount) + if partialMap[token].IsMax { + // do not attempt to add something to an already max float + continue + } + + partialMap[token].Add(partialMap[token].Float, amount) } return partialMap, nil } + +func isMaxValueString(value string) bool { + value = strings.ToLower(value) + + for _, maxValue := range maxValues { + if value == maxValue { + return true + } + } + + return false +} diff --git a/executors/ethereum/common_test.go b/executors/ethereum/common_test.go index 12b75a1f..a2fb424d 100644 --- a/executors/ethereum/common_test.go +++ b/executors/ethereum/common_test.go @@ -120,11 +120,29 @@ func TestConvertPartialMigrationStringToMap(t *testing.T) { results, err := ConvertPartialMigrationStringToMap(str) assert.Nil(t, err) - expectedResults := map[string]*big.Float{ - "k": big.NewFloat(3.2), - "f": big.NewFloat(1), - "g": big.NewFloat(0.001), - "h": big.NewFloat(0), + expectedResults := map[string]*FloatWrapper{ + "k": {Float: big.NewFloat(3.2)}, + "f": {Float: big.NewFloat(1)}, + "g": {Float: big.NewFloat(0.001)}, + "h": {Float: big.NewFloat(0)}, + } + + assert.Nil(t, err) + assert.Equal(t, expectedResults, results) + }) + t.Run("should work with maximum available", func(t *testing.T) { + t.Parallel() + + str := "k:1,l:0,m:*,n:AlL,o:MaX" + results, err := ConvertPartialMigrationStringToMap(str) + assert.Nil(t, err) + + expectedResults := map[string]*FloatWrapper{ + "k": {Float: big.NewFloat(1)}, + "l": {Float: big.NewFloat(0), IsMax: false}, + "m": {Float: big.NewFloat(0), IsMax: true}, + "n": {Float: big.NewFloat(0), IsMax: true}, + "o": {Float: big.NewFloat(0), IsMax: true}, } assert.Nil(t, err) diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go index 9e88271f..f3cf7fad 100644 --- a/executors/ethereum/migrationBatchCreator.go +++ b/executors/ethereum/migrationBatchCreator.go @@ -60,7 +60,7 @@ func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCr } // CreateBatchInfo creates an instance of type BatchInfo -func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*big.Float) (*BatchInfo, error) { +func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*FloatWrapper) (*BatchInfo, error) { creator.logger.Info("started the batch creation process...") depositStart := uint64(0) // deposits inside a batch are not tracked, we can start from 0 @@ -78,7 +78,7 @@ func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSa "free batch ID", freeBatchID, "time took", endTime.Sub(startTime)) if partialMigration == nil { - partialMigration = make(map[string]*big.Float) + partialMigration = make(map[string]*FloatWrapper) } tokensList, err := creator.getTokensList(ctx, partialMigration) @@ -175,7 +175,7 @@ func (creator *migrationBatchCreator) checkAvailableBatch( return nil } -func (creator *migrationBatchCreator) getTokensList(ctx context.Context, partialMigration map[string]*big.Float) ([]string, error) { +func (creator *migrationBatchCreator) getTokensList(ctx context.Context, partialMigration map[string]*FloatWrapper) ([]string, error) { tokens, err := creator.mvxDataGetter.GetAllKnownTokens(ctx) if err != nil { return nil, err @@ -224,7 +224,7 @@ func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.C return deposits, nil } -func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposits []*DepositInfo, partialMigration map[string]*big.Float) error { +func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposits []*DepositInfo, partialMigration map[string]*FloatWrapper) error { for _, deposit := range deposits { balance, err := creator.erc20ContractsHolder.BalanceOf(ctx, deposit.ContractAddress, creator.safeContractAddress) if err != nil { @@ -237,9 +237,10 @@ func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposit } deposit.Decimals = decimals - trimAmount := partialMigration[deposit.Token] - if trimAmount != nil { - denominatedTrimAmount := big.NewFloat(0).Set(trimAmount) + trimValue := partialMigration[deposit.Token] + trimIsNeeded := trimValue != nil && !trimValue.IsMax + if trimIsNeeded { + denominatedTrimAmount := big.NewFloat(0).Set(trimValue.Float) multiplier := big.NewInt(10) multiplier.Exp(multiplier, big.NewInt(int64(deposit.Decimals)), nil) denominatedTrimAmount.Mul(denominatedTrimAmount, big.NewFloat(0).SetInt(multiplier)) diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go index 277aa319..7a9fe0de 100644 --- a/executors/ethereum/migrationBatchCreator_test.go +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -561,11 +561,65 @@ func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { expectedBatch.DepositsInfo[1].DenominatedAmount, _ = big.NewFloat(0).SetString("0.000000000000000020") expectedBatch.DepositsInfo[2].DenominatedAmount, _ = big.NewFloat(0).SetString("120") - partialMap := map[string]*big.Float{ - "tkn1": big.NewFloat(0.017), - "tkn3": big.NewFloat(120), + token2Value, _ := big.NewFloat(0).SetString("0.000000000000000020") + partialMap := map[string]*FloatWrapper{ + "tkn1": {Float: big.NewFloat(0.017)}, + "tkn2": {Float: token2Value}, + "tkn3": {Float: big.NewFloat(120)}, + } + + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, partialMap) + assert.Nil(t, err) + assert.Equal(t, expectedBatch, batch) + }) + t.Run("with trim and all quantity", func(t *testing.T) { + expectedBatch := &BatchInfo{ + OldSafeContractAddress: safeContractAddress.String(), + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: firstFreeBatchId, + MessageHash: common.HexToHash("0x8c1b5bb16418a3dec1990fe8c0cd8363e9e56ca6870b6c7f0e7496f4411f60b0"), + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 1, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(17), + AmountString: "17", + DenominatedAmountString: "0.017", + Decimals: 3, + }, + { + DepositNonce: 2, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(38), + AmountString: "38", + DenominatedAmountString: "0.000000000000000038", + Decimals: 18, + }, + { + DepositNonce: 3, + Token: "tkn3", + ContractAddressString: common.BytesToAddress(tkn3Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn3Erc20Address), + Amount: big.NewInt(120), + AmountString: "120", + DenominatedAmountString: "120", + Decimals: 0, + }, + }, + } + expectedBatch.DepositsInfo[0].DenominatedAmount, _ = big.NewFloat(0).SetString("0.017") + expectedBatch.DepositsInfo[1].DenominatedAmount, _ = big.NewFloat(0).SetString("0.000000000000000038") + expectedBatch.DepositsInfo[2].DenominatedAmount, _ = big.NewFloat(0).SetString("120") + + partialMap := map[string]*FloatWrapper{ + "tkn1": {Float: big.NewFloat(0.017)}, + "tkn2": {Float: big.NewFloat(0), IsMax: true}, + "tkn3": {Float: big.NewFloat(120)}, } - partialMap["tkn2"], _ = big.NewFloat(0).SetString("0.000000000000000020") batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, partialMap) assert.Nil(t, err) From d4f7ed89e0ee708f59c55581db72bd7553c96599 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Thu, 14 Nov 2024 13:44:34 +0200 Subject: [PATCH 2/2] - added a missing unit test --- executors/ethereum/common_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/executors/ethereum/common_test.go b/executors/ethereum/common_test.go index a2fb424d..d5c55b13 100644 --- a/executors/ethereum/common_test.go +++ b/executors/ethereum/common_test.go @@ -145,6 +145,23 @@ func TestConvertPartialMigrationStringToMap(t *testing.T) { "o": {Float: big.NewFloat(0), IsMax: true}, } + assert.Nil(t, err) + assert.Equal(t, expectedResults, results) + }) + t.Run("should not add on a token with max value", func(t *testing.T) { + t.Parallel() + + str := "k:1,l:0,m:*,k:*,n:1,k:4" + results, err := ConvertPartialMigrationStringToMap(str) + assert.Nil(t, err) + + expectedResults := map[string]*FloatWrapper{ + "k": {Float: big.NewFloat(0), IsMax: true}, + "l": {Float: big.NewFloat(0), IsMax: false}, + "m": {Float: big.NewFloat(0), IsMax: true}, + "n": {Float: big.NewFloat(1), IsMax: false}, + } + assert.Nil(t, err) assert.Equal(t, expectedResults, results) })