From 46fb7878bafee7e875f8660437ab95d2bda3f91a Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 30 Sep 2024 11:44:21 -0300 Subject: [PATCH 1/2] chore(evm): adjust extra decimals on stateDB methods --- x/evm/statedb/state_object.go | 3 ++ x/evm/wrappers/bank.go | 10 +++--- x/evm/wrappers/utils.go | 27 ++++++++++---- x/evm/wrappers/utils_test.go | 66 ++++++++++++++++++++++++++++++++--- 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/x/evm/statedb/state_object.go b/x/evm/statedb/state_object.go index b69d1b76d0..2ab88236c4 100644 --- a/x/evm/statedb/state_object.go +++ b/x/evm/statedb/state_object.go @@ -11,6 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" "github.com/evmos/evmos/v20/x/evm/types" + "github.com/evmos/evmos/v20/x/evm/wrappers" ) // Account is the Ethereum consensus representation of accounts. @@ -100,6 +101,7 @@ func (s *stateObject) markSuicided() { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. func (s *stateObject) AddBalance(amount *big.Int) { + amount = wrappers.AdjustExtraDecimalsBigInt(amount) if amount.Sign() == 0 { return } @@ -109,6 +111,7 @@ func (s *stateObject) AddBalance(amount *big.Int) { // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. func (s *stateObject) SubBalance(amount *big.Int) { + amount = wrappers.AdjustExtraDecimalsBigInt(amount) if amount.Sign() == 0 { return } diff --git a/x/evm/wrappers/bank.go b/x/evm/wrappers/bank.go index 89cf30b415..879cef978b 100644 --- a/x/evm/wrappers/bank.go +++ b/x/evm/wrappers/bank.go @@ -42,7 +42,7 @@ func NewBankWrapper( func (w BankWrapper) MintAmountToAccount(ctx context.Context, recipientAddr sdk.AccAddress, amt *big.Int) error { coin := sdk.Coin{Denom: config.GetEVMCoinDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} - convertedCoin, err := convertEvmCoinFrom18Decimals(coin) + convertedCoin, err := ConvertEvmCoinFrom18Decimals(coin) if err != nil { return errors.Wrap(err, "failed to mint coin to account in bank wrapper") } @@ -60,7 +60,7 @@ func (w BankWrapper) MintAmountToAccount(ctx context.Context, recipientAddr sdk. func (w BankWrapper) BurnAmountFromAccount(ctx context.Context, account sdk.AccAddress, amt *big.Int) error { coin := sdk.Coin{Denom: config.GetEVMCoinDenom(), Amount: sdkmath.NewIntFromBigInt(amt)} - convertedCoin, err := convertEvmCoinFrom18Decimals(coin) + convertedCoin, err := ConvertEvmCoinFrom18Decimals(coin) if err != nil { return errors.Wrap(err, "failed to burn coins from account in bank wrapper") } @@ -83,14 +83,14 @@ func (w BankWrapper) GetBalance(ctx context.Context, addr sdk.AccAddress, denom // representation used in the evm. coin := w.BankKeeper.GetBalance(ctx, addr, denom) - return mustConvertEvmCoinTo18Decimals(coin) + return MustConvertEvmCoinTo18Decimals(coin) } // SendCoinsFromAccountToModule wraps around the Cosmos SDK x/bank module's // SendCoinsFromAccountToModule method to convert the evm coin, if present in // the input, to its original representation. func (w BankWrapper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, coins sdk.Coins) error { - convertedCoins := convertCoinsFrom18Decimals(coins) + convertedCoins := ConvertCoinsFrom18Decimals(coins) return w.BankKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, convertedCoins) } @@ -99,7 +99,7 @@ func (w BankWrapper) SendCoinsFromAccountToModule(ctx context.Context, senderAdd // SendCoinsFromModuleToAccount method to convert the evm coin, if present in // the input, to its original representation. func (w BankWrapper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, coins sdk.Coins) error { - convertedCoins := convertCoinsFrom18Decimals(coins) + convertedCoins := ConvertCoinsFrom18Decimals(coins) return w.BankKeeper.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, convertedCoins) } diff --git a/x/evm/wrappers/utils.go b/x/evm/wrappers/utils.go index a584f08252..a147d937bc 100644 --- a/x/evm/wrappers/utils.go +++ b/x/evm/wrappers/utils.go @@ -5,16 +5,17 @@ package wrappers import ( "fmt" + "math/big" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/evmos/evmos/v20/x/evm/config" ) -// mustConvertEvmCoinTo18Decimals converts the coin's Amount from its original +// MustConvertEvmCoinTo18Decimals converts the coin's Amount from its original // representation into a 18 decimals. The function panics if coin denom is // not the evm denom or in case of overflow. -func mustConvertEvmCoinTo18Decimals(coin sdk.Coin) sdk.Coin { +func MustConvertEvmCoinTo18Decimals(coin sdk.Coin) sdk.Coin { if coin.Denom != config.GetEVMCoinDenom() { panic(fmt.Sprintf("expected evm denom %s, received %s", config.GetEVMCoinDenom(), coin.Denom)) } @@ -25,9 +26,9 @@ func mustConvertEvmCoinTo18Decimals(coin sdk.Coin) sdk.Coin { return sdk.Coin{Denom: coin.Denom, Amount: newAmount} } -// convertEvmCoinFrom18Decimals converts the coin's Amount from 18 decimals to its +// ConvertEvmCoinFrom18Decimals converts the coin's Amount from 18 decimals to its // original representation. Return an error if the coin denom is not the EVM. -func convertEvmCoinFrom18Decimals(coin sdk.Coin) (sdk.Coin, error) { +func ConvertEvmCoinFrom18Decimals(coin sdk.Coin) (sdk.Coin, error) { if coin.Denom != config.GetEVMCoinDenom() { return sdk.Coin{}, fmt.Errorf("expected coin denom %s, received %s", config.GetEVMCoinDenom(), coin.Denom) } @@ -38,9 +39,9 @@ func convertEvmCoinFrom18Decimals(coin sdk.Coin) (sdk.Coin, error) { return sdk.Coin{Denom: coin.Denom, Amount: newAmount}, nil } -// convertCoinsFrom18Decimals returns the given coins with the Amount of the evm +// ConvertCoinsFrom18Decimals returns the given coins with the Amount of the evm // coin converted from the 18 decimals representation to the original one. -func convertCoinsFrom18Decimals(coins sdk.Coins) sdk.Coins { +func ConvertCoinsFrom18Decimals(coins sdk.Coins) sdk.Coins { evmDenom := config.GetEVMCoinDenom() convertedCoins := make(sdk.Coins, len(coins)) @@ -56,3 +57,17 @@ func convertCoinsFrom18Decimals(coins sdk.Coins) sdk.Coins { } return convertedCoins } + +// AdjustExtraDecimalsBigInt replaces all extra decimals by zero of an amount with 18 decimals in big.Int when having a decimal configuration different than 18 decimals +func AdjustExtraDecimalsBigInt(amt *big.Int) *big.Int { + if amt.Sign() == 0 { + return amt + } + dec := config.GetEVMCoinDecimals() + if dec == config.EighteenDecimals { + return amt + } + scaleFactor := dec.ConversionFactor() + scaledDown := new(big.Int).Quo(amt, scaleFactor.BigInt()) + return new(big.Int).Mul(scaledDown, scaleFactor.BigInt()) +} diff --git a/x/evm/wrappers/utils_test.go b/x/evm/wrappers/utils_test.go index dc93eb4bcd..f3305d0b5c 100644 --- a/x/evm/wrappers/utils_test.go +++ b/x/evm/wrappers/utils_test.go @@ -1,15 +1,18 @@ // Copyright Tharsis Labs Ltd.(Evmos) // SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) -package wrappers +package wrappers_test import ( + "fmt" + "math/big" "testing" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/evmos/evmos/v20/types" "github.com/evmos/evmos/v20/x/evm/config" + "github.com/evmos/evmos/v20/x/evm/wrappers" "github.com/stretchr/testify/require" ) @@ -76,7 +79,7 @@ func TestMustConvertEvmCoinTo18Decimals(t *testing.T) { config.SetEVMCoinTEST(tc.evmCoinInfo) - coinConverted := mustConvertEvmCoinTo18Decimals(tc.coin) + coinConverted := wrappers.MustConvertEvmCoinTo18Decimals(tc.coin) if !tc.expPanic { require.Equal(t, tc.expCoin, coinConverted, "expected a different coin") @@ -140,7 +143,7 @@ func TestConvertEvmCoinFrom18Decimals(t *testing.T) { t.Run(tc.name, func(t *testing.T) { config.SetEVMCoinTEST(tc.evmCoinInfo) - coinConverted, err := convertEvmCoinFrom18Decimals(tc.coin) + coinConverted, err := wrappers.ConvertEvmCoinFrom18Decimals(tc.coin) if !tc.expErr { require.NoError(t, err) @@ -198,8 +201,63 @@ func TestConvertCoinsFrom18Decimals(t *testing.T) { t.Run(tc.name, func(t *testing.T) { config.SetEVMCoinTEST(tc.evmCoinInfo) - coinConverted := convertCoinsFrom18Decimals(tc.coins) + coinConverted := wrappers.ConvertCoinsFrom18Decimals(tc.coins) require.Equal(t, tc.expCoins, coinConverted, "expected a different coin") }) } } + +func TestZeroExtraDecimalsBigInt(t *testing.T) { + testCases := []struct { + name string + amt *big.Int + exp *big.Int + }{ + { + name: "almost 1: 0.99999...", + amt: big.NewInt(999999999999), + exp: big.NewInt(0), + }, + { + name: "decimal < 5: 1.4", + amt: big.NewInt(14e11), + exp: big.NewInt(1e12), + }, + { + name: "decimal < 5: 1.499999999999", + amt: big.NewInt(1499999999999), + exp: big.NewInt(1e12), + }, + { + name: "decimal == 5: 1.5", + amt: big.NewInt(15e11), + exp: big.NewInt(1e12), + }, + { + name: "decimal > 5: 1.9", + amt: big.NewInt(19e11), + exp: big.NewInt(1e12), + }, + { + name: "1 wei", + amt: big.NewInt(1), + exp: big.NewInt(0), + }, + } + + for _, cfg := range []config.EvmCoinInfo{ + {Denom: types.BaseDenom, Decimals: config.SixDecimals}, + {Denom: types.BaseDenom, Decimals: config.EighteenDecimals}, + } { + for _, tc := range testCases { + t.Run(fmt.Sprintf("%d dec - %s", cfg.Decimals, tc.name), func(t *testing.T) { + config.SetEVMCoinTEST(cfg) + res := wrappers.AdjustExtraDecimalsBigInt(tc.amt) + if cfg.Decimals == config.EighteenDecimals { + tc.exp = tc.amt + } + require.Equal(t, tc.exp, res) + }) + } + } +} From 53d67af54ac0322b65369b8bbaed01756edd6e40 Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 30 Sep 2024 11:47:30 -0300 Subject: [PATCH 2/2] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc07a8314..f72abcffec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - (evm) [#2872](https://github.com/evmos/evmos/pull/2872) Change `BankKeeper` for `BankWrapper` to manage EVM coin with different decimals. - (evm) [#2873](https://github.com/evmos/evmos/pull/2873) Remove `BeginBlock` and replace `ChainID` with global config. - (api) [#2884](https://github.com/evmos/evmos/pull/2884) Refactor to use a function to return `TxDataV2` in custom `GetSingers`. +- (evm) [#2897](https://github.com/evmos/evmos/pull/2897) Adjust extra decimals in `AddBalance` and `SubBalance` methods on `stateObject` in state DB. ## [v19.2.1](https://github.com/evmos/evmos/releases/tag/v19.2.1) - 2024-09-27