Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration support for all balance #377

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/migration/config/config-mainnet-eth.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions cmd/migration/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
}
29 changes: 29 additions & 0 deletions cmd/migration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down
47 changes: 42 additions & 5 deletions executors/ethereum/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -69,32 +77,61 @@ 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)
if len(splt) != 2 {
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
}
45 changes: 40 additions & 5 deletions executors/ethereum/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,46 @@ 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)
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)
Expand Down
15 changes: 8 additions & 7 deletions executors/ethereum/migrationBatchCreator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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))
Expand Down
62 changes: 58 additions & 4 deletions executors/ethereum/migrationBatchCreator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading