mirror of
https://github.com/0glabs/0g-chain.git
synced 2025-01-18 02:55:18 +00:00
feat: Use x/precisebank for x/evm keeper (#1960)
Replace x/evmutil EvmBankKeeper usage for x/evm
This commit is contained in:
parent
9de9de671e
commit
d2d661276e
@ -550,7 +550,6 @@ func NewApp(
|
||||
app.accountKeeper,
|
||||
)
|
||||
|
||||
// TODO: Pass this to evmkeeper.NewKeeper() instead of evmutilKeeper
|
||||
app.precisebankKeeper = precisebankkeeper.NewKeeper(
|
||||
app.appCodec,
|
||||
keys[precisebanktypes.StoreKey],
|
||||
@ -558,11 +557,13 @@ func NewApp(
|
||||
app.accountKeeper,
|
||||
)
|
||||
|
||||
evmBankKeeper := evmutilkeeper.NewEvmBankKeeper(app.evmutilKeeper, app.bankKeeper, app.accountKeeper)
|
||||
app.evmKeeper = evmkeeper.NewKeeper(
|
||||
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey],
|
||||
govAuthAddr,
|
||||
app.accountKeeper, evmBankKeeper, app.stakingKeeper, app.feeMarketKeeper,
|
||||
app.accountKeeper,
|
||||
app.precisebankKeeper, // x/precisebank in place of x/bank
|
||||
app.stakingKeeper,
|
||||
app.feeMarketKeeper,
|
||||
nil, // precompiled contracts
|
||||
geth.NewEVM,
|
||||
options.EVMTrace,
|
||||
|
@ -1,281 +0,0 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/evmutil/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// EvmDenom is the gas denom used by the evm
|
||||
EvmDenom = "akava"
|
||||
|
||||
// CosmosDenom is the gas denom used by the kava app
|
||||
CosmosDenom = "ukava"
|
||||
)
|
||||
|
||||
// ConversionMultiplier is the conversion multiplier between akava and ukava
|
||||
var ConversionMultiplier = sdkmath.NewInt(1_000_000_000_000)
|
||||
|
||||
var _ evmtypes.BankKeeper = EvmBankKeeper{}
|
||||
|
||||
// EvmBankKeeper is a BankKeeper wrapper for the x/evm module to allow the use
|
||||
// of the 18 decimal akava coin on the evm.
|
||||
// x/evm consumes gas and send coins by minting and burning akava coins in its module
|
||||
// account and then sending the funds to the target account.
|
||||
// This keeper uses both the ukava coin and a separate akava balance to manage the
|
||||
// extra precision needed by the evm.
|
||||
type EvmBankKeeper struct {
|
||||
akavaKeeper Keeper
|
||||
bk types.BankKeeper
|
||||
ak types.AccountKeeper
|
||||
}
|
||||
|
||||
func NewEvmBankKeeper(akavaKeeper Keeper, bk types.BankKeeper, ak types.AccountKeeper) EvmBankKeeper {
|
||||
return EvmBankKeeper{
|
||||
akavaKeeper: akavaKeeper,
|
||||
bk: bk,
|
||||
ak: ak,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBalance returns the total **spendable** balance of akava for a given account by address.
|
||||
func (k EvmBankKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin {
|
||||
if denom != EvmDenom {
|
||||
panic(fmt.Errorf("only evm denom %s is supported by EvmBankKeeper", EvmDenom))
|
||||
}
|
||||
|
||||
spendableCoins := k.bk.SpendableCoins(ctx, addr)
|
||||
ukava := spendableCoins.AmountOf(CosmosDenom)
|
||||
akava := k.akavaKeeper.GetBalance(ctx, addr)
|
||||
total := ukava.Mul(ConversionMultiplier).Add(akava)
|
||||
return sdk.NewCoin(EvmDenom, total)
|
||||
}
|
||||
|
||||
// SendCoins transfers akava coins from a AccAddress to an AccAddress.
|
||||
func (k EvmBankKeeper) SendCoins(ctx sdk.Context, senderAddr sdk.AccAddress, recipientAddr sdk.AccAddress, amt sdk.Coins) error {
|
||||
// SendCoins method is not used by the evm module, but is required by the
|
||||
// evmtypes.BankKeeper interface. This must be updated if the evm module
|
||||
// is updated to use SendCoins.
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// SendCoinsFromModuleToAccount transfers akava coins from a ModuleAccount to an AccAddress.
|
||||
// It will panic if the module account does not exist. An error is returned if the recipient
|
||||
// address is black-listed or if sending the tokens fails.
|
||||
func (k EvmBankKeeper) SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error {
|
||||
ukava, akava, err := SplitAkavaCoins(amt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ukava.Amount.IsPositive() {
|
||||
if err := k.bk.SendCoinsFromModuleToAccount(ctx, senderModule, recipientAddr, sdk.NewCoins(ukava)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
senderAddr := k.GetModuleAddress(senderModule)
|
||||
if err := k.ConvertOneUkavaToAkavaIfNeeded(ctx, senderAddr, akava); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := k.akavaKeeper.SendBalance(ctx, senderAddr, recipientAddr, akava); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.ConvertAkavaToUkava(ctx, recipientAddr)
|
||||
}
|
||||
|
||||
// SendCoinsFromAccountToModule transfers akava coins from an AccAddress to a ModuleAccount.
|
||||
// It will panic if the module account does not exist.
|
||||
func (k EvmBankKeeper) SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error {
|
||||
ukava, akavaNeeded, err := SplitAkavaCoins(amt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ukava.IsPositive() {
|
||||
if err := k.bk.SendCoinsFromAccountToModule(ctx, senderAddr, recipientModule, sdk.NewCoins(ukava)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := k.ConvertOneUkavaToAkavaIfNeeded(ctx, senderAddr, akavaNeeded); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recipientAddr := k.GetModuleAddress(recipientModule)
|
||||
if err := k.akavaKeeper.SendBalance(ctx, senderAddr, recipientAddr, akavaNeeded); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.ConvertAkavaToUkava(ctx, recipientAddr)
|
||||
}
|
||||
|
||||
// MintCoins mints akava coins by minting the equivalent ukava coins and any remaining akava coins.
|
||||
// It will panic if the module account does not exist or is unauthorized.
|
||||
func (k EvmBankKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error {
|
||||
ukava, akava, err := SplitAkavaCoins(amt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ukava.IsPositive() {
|
||||
if err := k.bk.MintCoins(ctx, moduleName, sdk.NewCoins(ukava)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
recipientAddr := k.GetModuleAddress(moduleName)
|
||||
if err := k.akavaKeeper.AddBalance(ctx, recipientAddr, akava); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.ConvertAkavaToUkava(ctx, recipientAddr)
|
||||
}
|
||||
|
||||
// BurnCoins burns akava coins by burning the equivalent ukava coins and any remaining akava coins.
|
||||
// It will panic if the module account does not exist or is unauthorized.
|
||||
func (k EvmBankKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error {
|
||||
ukava, akava, err := SplitAkavaCoins(amt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ukava.IsPositive() {
|
||||
if err := k.bk.BurnCoins(ctx, moduleName, sdk.NewCoins(ukava)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
moduleAddr := k.GetModuleAddress(moduleName)
|
||||
if err := k.ConvertOneUkavaToAkavaIfNeeded(ctx, moduleAddr, akava); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.akavaKeeper.RemoveBalance(ctx, moduleAddr, akava)
|
||||
}
|
||||
|
||||
// IsSendEnabledCoins checks the coins provided and returns an ErrSendDisabled
|
||||
// if any of the coins are not configured for sending. Returns nil if sending is
|
||||
// enabled for all provided coins.
|
||||
func (k EvmBankKeeper) IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error {
|
||||
// IsSendEnabledCoins method is not used by the evm module, but is required by the
|
||||
// evmtypes.BankKeeper interface. This must be updated if the evm module
|
||||
// is updated to use IsSendEnabledCoins.
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// ConvertOneUkavaToAkavaIfNeeded converts 1 ukava to akava for an address if
|
||||
// its akava balance is smaller than the akavaNeeded amount.
|
||||
func (k EvmBankKeeper) ConvertOneUkavaToAkavaIfNeeded(ctx sdk.Context, addr sdk.AccAddress, akavaNeeded sdkmath.Int) error {
|
||||
akavaBal := k.akavaKeeper.GetBalance(ctx, addr)
|
||||
if akavaBal.GTE(akavaNeeded) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ukavaToStore := sdk.NewCoins(sdk.NewCoin(CosmosDenom, sdk.OneInt()))
|
||||
if err := k.bk.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, ukavaToStore); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add 1ukava equivalent of akava to addr
|
||||
akavaToReceive := ConversionMultiplier
|
||||
if err := k.akavaKeeper.AddBalance(ctx, addr, akavaToReceive); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertAkavaToUkava converts all available akava to ukava for a given AccAddress.
|
||||
func (k EvmBankKeeper) ConvertAkavaToUkava(ctx sdk.Context, addr sdk.AccAddress) error {
|
||||
totalAkava := k.akavaKeeper.GetBalance(ctx, addr)
|
||||
ukava, _, err := SplitAkavaCoins(sdk.NewCoins(sdk.NewCoin(EvmDenom, totalAkava)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// do nothing if account does not have enough akava for a single ukava
|
||||
ukavaToReceive := ukava.Amount
|
||||
if !ukavaToReceive.IsPositive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// remove akava used for converting to ukava
|
||||
akavaToBurn := ukavaToReceive.Mul(ConversionMultiplier)
|
||||
finalBal := totalAkava.Sub(akavaToBurn)
|
||||
if err := k.akavaKeeper.SetBalance(ctx, addr, finalBal); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromAddr := k.GetModuleAddress(types.ModuleName)
|
||||
if err := k.bk.SendCoins(ctx, fromAddr, addr, sdk.NewCoins(ukava)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k EvmBankKeeper) GetModuleAddress(moduleName string) sdk.AccAddress {
|
||||
addr := k.ak.GetModuleAddress(moduleName)
|
||||
if addr == nil {
|
||||
panic(errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", moduleName))
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// SplitAkavaCoins splits akava coins to the equivalent ukava coins and any remaining akava balance.
|
||||
// An error will be returned if the coins are not valid or if the coins are not the akava denom.
|
||||
func SplitAkavaCoins(coins sdk.Coins) (sdk.Coin, sdkmath.Int, error) {
|
||||
akava := sdk.ZeroInt()
|
||||
ukava := sdk.NewCoin(CosmosDenom, sdk.ZeroInt())
|
||||
|
||||
if len(coins) == 0 {
|
||||
return ukava, akava, nil
|
||||
}
|
||||
|
||||
if err := ValidateEvmCoins(coins); err != nil {
|
||||
return ukava, akava, err
|
||||
}
|
||||
|
||||
// note: we should always have len(coins) == 1 here since coins cannot have dup denoms after we validate.
|
||||
coin := coins[0]
|
||||
remainingBalance := coin.Amount.Mod(ConversionMultiplier)
|
||||
if remainingBalance.IsPositive() {
|
||||
akava = remainingBalance
|
||||
}
|
||||
ukavaAmount := coin.Amount.Quo(ConversionMultiplier)
|
||||
if ukavaAmount.IsPositive() {
|
||||
ukava = sdk.NewCoin(CosmosDenom, ukavaAmount)
|
||||
}
|
||||
|
||||
return ukava, akava, nil
|
||||
}
|
||||
|
||||
// ValidateEvmCoins validates the coins from evm is valid and is the EvmDenom (akava).
|
||||
func ValidateEvmCoins(coins sdk.Coins) error {
|
||||
if len(coins) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate that coins are non-negative, sorted, and no dup denoms
|
||||
if err := coins.Validate(); err != nil {
|
||||
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, coins.String())
|
||||
}
|
||||
|
||||
// validate that coin denom is akava
|
||||
if len(coins) != 1 || coins[0].Denom != EvmDenom {
|
||||
errMsg := fmt.Sprintf("invalid evm coin denom, only %s is supported", EvmDenom)
|
||||
return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, errMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,798 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
tmtime "github.com/cometbft/cometbft/types/time"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
vesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/evmutil/keeper"
|
||||
"github.com/kava-labs/kava/x/evmutil/testutil"
|
||||
"github.com/kava-labs/kava/x/evmutil/types"
|
||||
)
|
||||
|
||||
type evmBankKeeperTestSuite struct {
|
||||
testutil.Suite
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) SetupTest() {
|
||||
suite.Suite.SetupTest()
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestGetBalance_ReturnsSpendable() {
|
||||
startingCoins := sdk.NewCoins(sdk.NewInt64Coin("ukava", 10))
|
||||
startingAkava := sdkmath.NewInt(100)
|
||||
|
||||
now := tmtime.Now()
|
||||
endTime := now.Add(24 * time.Hour)
|
||||
bacc := authtypes.NewBaseAccountWithAddress(suite.Addrs[0])
|
||||
vacc := vesting.NewContinuousVestingAccount(bacc, startingCoins, now.Unix(), endTime.Unix())
|
||||
suite.AccountKeeper.SetAccount(suite.Ctx, vacc)
|
||||
|
||||
err := suite.App.FundAccount(suite.Ctx, suite.Addrs[0], startingCoins)
|
||||
suite.Require().NoError(err)
|
||||
err = suite.Keeper.SetBalance(suite.Ctx, suite.Addrs[0], startingAkava)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
coin := suite.EvmBankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "akava")
|
||||
suite.Require().Equal(startingAkava, coin.Amount)
|
||||
|
||||
ctx := suite.Ctx.WithBlockTime(now.Add(12 * time.Hour))
|
||||
coin = suite.EvmBankKeeper.GetBalance(ctx, suite.Addrs[0], "akava")
|
||||
suite.Require().Equal(sdkmath.NewIntFromUint64(5_000_000_000_100), coin.Amount)
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestGetBalance_NotEvmDenom() {
|
||||
suite.Require().Panics(func() {
|
||||
suite.EvmBankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "ukava")
|
||||
})
|
||||
suite.Require().Panics(func() {
|
||||
suite.EvmBankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "busd")
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestGetBalance() {
|
||||
tests := []struct {
|
||||
name string
|
||||
startingAmount sdk.Coins
|
||||
expAmount sdkmath.Int
|
||||
}{
|
||||
{
|
||||
"ukava with akava",
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 100),
|
||||
sdk.NewInt64Coin("ukava", 10),
|
||||
),
|
||||
sdkmath.NewInt(10_000_000_000_100),
|
||||
},
|
||||
{
|
||||
"just akava",
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 100),
|
||||
sdk.NewInt64Coin("busd", 100),
|
||||
),
|
||||
sdkmath.NewInt(100),
|
||||
},
|
||||
{
|
||||
"just ukava",
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("ukava", 10),
|
||||
sdk.NewInt64Coin("busd", 100),
|
||||
),
|
||||
sdkmath.NewInt(10_000_000_000_000),
|
||||
},
|
||||
{
|
||||
"no ukava or akava",
|
||||
sdk.NewCoins(),
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"with avaka that is more than 1 ukava",
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 20_000_000_000_220),
|
||||
sdk.NewInt64Coin("ukava", 11),
|
||||
),
|
||||
sdkmath.NewInt(31_000_000_000_220),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
suite.FundAccountWithKava(suite.Addrs[0], tt.startingAmount)
|
||||
coin := suite.EvmBankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "akava")
|
||||
suite.Require().Equal(tt.expAmount, coin.Amount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestSendCoinsFromModuleToAccount() {
|
||||
startingModuleCoins := sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 200),
|
||||
sdk.NewInt64Coin("ukava", 100),
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
sendCoins sdk.Coins
|
||||
startingAccBal sdk.Coins
|
||||
expAccBal sdk.Coins
|
||||
hasErr bool
|
||||
}{
|
||||
{
|
||||
"send more than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_000_000_000_010)),
|
||||
sdk.Coins{},
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 10),
|
||||
sdk.NewInt64Coin("ukava", 12),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send less than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 122)),
|
||||
sdk.Coins{},
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 122),
|
||||
sdk.NewInt64Coin("ukava", 0),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send an exact amount of ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 98_000_000_000_000)),
|
||||
sdk.Coins{},
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 0o0),
|
||||
sdk.NewInt64Coin("ukava", 98),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send no akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 0)),
|
||||
sdk.Coins{},
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 0),
|
||||
sdk.NewInt64Coin("ukava", 0),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"errors if sending other coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 500), sdk.NewInt64Coin("busd", 1000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"errors if not enough total akava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_001_000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"errors if not enough ukava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200_000_000_000_000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"converts receiver's akava to ukava if there's enough akava after the transfer",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 99_000_000_000_200)),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 999_999_999_900),
|
||||
sdk.NewInt64Coin("ukava", 1),
|
||||
),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 100),
|
||||
sdk.NewInt64Coin("ukava", 101),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"converts all of receiver's akava to ukava even if somehow receiver has more than 1ukava of akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_000_000_000_100)),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 5_999_999_999_990),
|
||||
sdk.NewInt64Coin("ukava", 1),
|
||||
),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 90),
|
||||
sdk.NewInt64Coin("ukava", 19),
|
||||
),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"swap 1 ukava for akava if module account doesn't have enough akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 99_000_000_001_000)),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 200),
|
||||
sdk.NewInt64Coin("ukava", 1),
|
||||
),
|
||||
sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 1200),
|
||||
sdk.NewInt64Coin("ukava", 100),
|
||||
),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
suite.FundAccountWithKava(suite.Addrs[0], tt.startingAccBal)
|
||||
suite.FundModuleAccountWithKava(evmtypes.ModuleName, startingModuleCoins)
|
||||
|
||||
// fund our module with some ukava to account for converting extra akava back to ukava
|
||||
suite.FundModuleAccountWithKava(types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("ukava", 10)))
|
||||
|
||||
err := suite.EvmBankKeeper.SendCoinsFromModuleToAccount(suite.Ctx, evmtypes.ModuleName, suite.Addrs[0], tt.sendCoins)
|
||||
if tt.hasErr {
|
||||
suite.Require().Error(err)
|
||||
return
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// check ukava
|
||||
ukavaSender := suite.BankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "ukava")
|
||||
suite.Require().Equal(tt.expAccBal.AmountOf("ukava").Int64(), ukavaSender.Amount.Int64())
|
||||
|
||||
// check akava
|
||||
actualAkava := suite.Keeper.GetBalance(suite.Ctx, suite.Addrs[0])
|
||||
suite.Require().Equal(tt.expAccBal.AmountOf("akava").Int64(), actualAkava.Int64())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestSendCoinsFromAccountToModule() {
|
||||
startingAccCoins := sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 200),
|
||||
sdk.NewInt64Coin("ukava", 100),
|
||||
)
|
||||
startingModuleCoins := sdk.NewCoins(
|
||||
sdk.NewInt64Coin("akava", 100_000_000_000),
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
sendCoins sdk.Coins
|
||||
expSenderCoins sdk.Coins
|
||||
expModuleCoins sdk.Coins
|
||||
hasErr bool
|
||||
}{
|
||||
{
|
||||
"send more than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_000_000_000_010)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 190), sdk.NewInt64Coin("ukava", 88)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_010), sdk.NewInt64Coin("ukava", 12)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send less than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 122)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 78), sdk.NewInt64Coin("ukava", 100)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_122), sdk.NewInt64Coin("ukava", 0)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send an exact amount of ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 98_000_000_000_000)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200), sdk.NewInt64Coin("ukava", 2)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_000), sdk.NewInt64Coin("ukava", 98)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"send no akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 0)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200), sdk.NewInt64Coin("ukava", 100)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_000), sdk.NewInt64Coin("ukava", 0)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"errors if sending other coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 500), sdk.NewInt64Coin("busd", 1000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"errors if have dup coins",
|
||||
sdk.Coins{
|
||||
sdk.NewInt64Coin("akava", 12_000_000_000_000),
|
||||
sdk.NewInt64Coin("akava", 2_000_000_000_000),
|
||||
},
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"errors if not enough total akava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_001_000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"errors if not enough ukava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200_000_000_000_000)),
|
||||
sdk.Coins{},
|
||||
sdk.Coins{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"converts 1 ukava to akava if not enough akava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 99_001_000_000_000)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 999_000_000_200), sdk.NewInt64Coin("ukava", 0)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 101_000_000_000), sdk.NewInt64Coin("ukava", 99)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"converts receiver's akava to ukava if there's enough akava after the transfer",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 5_900_000_000_200)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_000_000_000), sdk.NewInt64Coin("ukava", 94)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200), sdk.NewInt64Coin("ukava", 6)),
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
suite.FundAccountWithKava(suite.Addrs[0], startingAccCoins)
|
||||
suite.FundModuleAccountWithKava(evmtypes.ModuleName, startingModuleCoins)
|
||||
|
||||
err := suite.EvmBankKeeper.SendCoinsFromAccountToModule(suite.Ctx, suite.Addrs[0], evmtypes.ModuleName, tt.sendCoins)
|
||||
if tt.hasErr {
|
||||
suite.Require().Error(err)
|
||||
return
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// check sender balance
|
||||
ukavaSender := suite.BankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "ukava")
|
||||
suite.Require().Equal(tt.expSenderCoins.AmountOf("ukava").Int64(), ukavaSender.Amount.Int64())
|
||||
actualAkava := suite.Keeper.GetBalance(suite.Ctx, suite.Addrs[0])
|
||||
suite.Require().Equal(tt.expSenderCoins.AmountOf("akava").Int64(), actualAkava.Int64())
|
||||
|
||||
// check module balance
|
||||
moduleAddr := suite.AccountKeeper.GetModuleAddress(evmtypes.ModuleName)
|
||||
ukavaSender = suite.BankKeeper.GetBalance(suite.Ctx, moduleAddr, "ukava")
|
||||
suite.Require().Equal(tt.expModuleCoins.AmountOf("ukava").Int64(), ukavaSender.Amount.Int64())
|
||||
actualAkava = suite.Keeper.GetBalance(suite.Ctx, moduleAddr)
|
||||
suite.Require().Equal(tt.expModuleCoins.AmountOf("akava").Int64(), actualAkava.Int64())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestBurnCoins() {
|
||||
startingUkava := sdkmath.NewInt(100)
|
||||
tests := []struct {
|
||||
name string
|
||||
burnCoins sdk.Coins
|
||||
expUkava sdkmath.Int
|
||||
expAkava sdkmath.Int
|
||||
hasErr bool
|
||||
akavaStart sdkmath.Int
|
||||
}{
|
||||
{
|
||||
"burn more than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_021_000_000_002)),
|
||||
sdkmath.NewInt(88),
|
||||
sdkmath.NewInt(100_000_000_000),
|
||||
false,
|
||||
sdkmath.NewInt(121_000_000_002),
|
||||
},
|
||||
{
|
||||
"burn less than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 122)),
|
||||
sdkmath.NewInt(100),
|
||||
sdkmath.NewInt(878),
|
||||
false,
|
||||
sdkmath.NewInt(1000),
|
||||
},
|
||||
{
|
||||
"burn an exact amount of ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 98_000_000_000_000)),
|
||||
sdkmath.NewInt(2),
|
||||
sdkmath.NewInt(10),
|
||||
false,
|
||||
sdkmath.NewInt(10),
|
||||
},
|
||||
{
|
||||
"burn no akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 0)),
|
||||
startingUkava,
|
||||
sdk.ZeroInt(),
|
||||
false,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"errors if burning other coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 500), sdk.NewInt64Coin("busd", 1000)),
|
||||
startingUkava,
|
||||
sdkmath.NewInt(100),
|
||||
true,
|
||||
sdkmath.NewInt(100),
|
||||
},
|
||||
{
|
||||
"errors if have dup coins",
|
||||
sdk.Coins{
|
||||
sdk.NewInt64Coin("akava", 12_000_000_000_000),
|
||||
sdk.NewInt64Coin("akava", 2_000_000_000_000),
|
||||
},
|
||||
startingUkava,
|
||||
sdk.ZeroInt(),
|
||||
true,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"errors if burn amount is negative",
|
||||
sdk.Coins{sdk.Coin{Denom: "akava", Amount: sdkmath.NewInt(-100)}},
|
||||
startingUkava,
|
||||
sdkmath.NewInt(50),
|
||||
true,
|
||||
sdkmath.NewInt(50),
|
||||
},
|
||||
{
|
||||
"errors if not enough akava to cover burn",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100_999_000_000_000)),
|
||||
sdkmath.NewInt(0),
|
||||
sdkmath.NewInt(99_000_000_000),
|
||||
true,
|
||||
sdkmath.NewInt(99_000_000_000),
|
||||
},
|
||||
{
|
||||
"errors if not enough ukava to cover burn",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 200_000_000_000_000)),
|
||||
sdkmath.NewInt(100),
|
||||
sdk.ZeroInt(),
|
||||
true,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"converts 1 ukava to akava if not enough akava to cover",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_021_000_000_002)),
|
||||
sdkmath.NewInt(87),
|
||||
sdkmath.NewInt(980_000_000_000),
|
||||
false,
|
||||
sdkmath.NewInt(1_000_000_002),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
startingCoins := sdk.NewCoins(
|
||||
sdk.NewCoin("ukava", startingUkava),
|
||||
sdk.NewCoin("akava", tt.akavaStart),
|
||||
)
|
||||
suite.FundModuleAccountWithKava(evmtypes.ModuleName, startingCoins)
|
||||
|
||||
err := suite.EvmBankKeeper.BurnCoins(suite.Ctx, evmtypes.ModuleName, tt.burnCoins)
|
||||
if tt.hasErr {
|
||||
suite.Require().Error(err)
|
||||
return
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// check ukava
|
||||
ukavaActual := suite.BankKeeper.GetBalance(suite.Ctx, suite.EvmModuleAddr, "ukava")
|
||||
suite.Require().Equal(tt.expUkava, ukavaActual.Amount)
|
||||
|
||||
// check akava
|
||||
akavaActual := suite.Keeper.GetBalance(suite.Ctx, suite.EvmModuleAddr)
|
||||
suite.Require().Equal(tt.expAkava, akavaActual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestMintCoins() {
|
||||
tests := []struct {
|
||||
name string
|
||||
mintCoins sdk.Coins
|
||||
ukava sdkmath.Int
|
||||
akava sdkmath.Int
|
||||
hasErr bool
|
||||
akavaStart sdkmath.Int
|
||||
}{
|
||||
{
|
||||
"mint more than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_021_000_000_002)),
|
||||
sdkmath.NewInt(12),
|
||||
sdkmath.NewInt(21_000_000_002),
|
||||
false,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"mint less than 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 901_000_000_001)),
|
||||
sdk.ZeroInt(),
|
||||
sdkmath.NewInt(901_000_000_001),
|
||||
false,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"mint an exact amount of ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 123_000_000_000_000_000)),
|
||||
sdkmath.NewInt(123_000),
|
||||
sdk.ZeroInt(),
|
||||
false,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"mint no akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 0)),
|
||||
sdk.ZeroInt(),
|
||||
sdk.ZeroInt(),
|
||||
false,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"errors if minting other coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 500), sdk.NewInt64Coin("busd", 1000)),
|
||||
sdk.ZeroInt(),
|
||||
sdkmath.NewInt(100),
|
||||
true,
|
||||
sdkmath.NewInt(100),
|
||||
},
|
||||
{
|
||||
"errors if have dup coins",
|
||||
sdk.Coins{
|
||||
sdk.NewInt64Coin("akava", 12_000_000_000_000),
|
||||
sdk.NewInt64Coin("akava", 2_000_000_000_000),
|
||||
},
|
||||
sdk.ZeroInt(),
|
||||
sdk.ZeroInt(),
|
||||
true,
|
||||
sdk.ZeroInt(),
|
||||
},
|
||||
{
|
||||
"errors if mint amount is negative",
|
||||
sdk.Coins{sdk.Coin{Denom: "akava", Amount: sdkmath.NewInt(-100)}},
|
||||
sdk.ZeroInt(),
|
||||
sdkmath.NewInt(50),
|
||||
true,
|
||||
sdkmath.NewInt(50),
|
||||
},
|
||||
{
|
||||
"adds to existing akava balance",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 12_021_000_000_002)),
|
||||
sdkmath.NewInt(12),
|
||||
sdkmath.NewInt(21_000_000_102),
|
||||
false,
|
||||
sdkmath.NewInt(100),
|
||||
},
|
||||
{
|
||||
"convert akava balance to ukava if it exceeds 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 10_999_000_000_000)),
|
||||
sdkmath.NewInt(12),
|
||||
sdkmath.NewInt(1_200_000_001),
|
||||
false,
|
||||
sdkmath.NewInt(1_002_200_000_001),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
suite.FundModuleAccountWithKava(types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("ukava", 10)))
|
||||
suite.FundModuleAccountWithKava(evmtypes.ModuleName, sdk.NewCoins(sdk.NewCoin("akava", tt.akavaStart)))
|
||||
|
||||
err := suite.EvmBankKeeper.MintCoins(suite.Ctx, evmtypes.ModuleName, tt.mintCoins)
|
||||
if tt.hasErr {
|
||||
suite.Require().Error(err)
|
||||
return
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// check ukava
|
||||
ukavaActual := suite.BankKeeper.GetBalance(suite.Ctx, suite.EvmModuleAddr, "ukava")
|
||||
suite.Require().Equal(tt.ukava, ukavaActual.Amount)
|
||||
|
||||
// check akava
|
||||
akavaActual := suite.Keeper.GetBalance(suite.Ctx, suite.EvmModuleAddr)
|
||||
suite.Require().Equal(tt.akava, akavaActual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestValidateEvmCoins() {
|
||||
tests := []struct {
|
||||
name string
|
||||
coins sdk.Coins
|
||||
shouldErr bool
|
||||
}{
|
||||
{
|
||||
"valid coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 500)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"dup coins",
|
||||
sdk.Coins{sdk.NewInt64Coin("akava", 500), sdk.NewInt64Coin("akava", 500)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"not evm coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 500)),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"negative coins",
|
||||
sdk.Coins{sdk.Coin{Denom: "akava", Amount: sdkmath.NewInt(-500)}},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
err := keeper.ValidateEvmCoins(tt.coins)
|
||||
if tt.shouldErr {
|
||||
suite.Require().Error(err)
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestConvertOneUkavaToAkavaIfNeeded() {
|
||||
akavaNeeded := sdkmath.NewInt(200)
|
||||
tests := []struct {
|
||||
name string
|
||||
startingCoins sdk.Coins
|
||||
expectedCoins sdk.Coins
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
"not enough ukava for conversion",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"converts 1 ukava to akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 10), sdk.NewInt64Coin("akava", 100)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 9), sdk.NewInt64Coin("akava", 1_000_000_000_100)),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"conversion not needed",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 10), sdk.NewInt64Coin("akava", 200)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 10), sdk.NewInt64Coin("akava", 200)),
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
suite.FundAccountWithKava(suite.Addrs[0], tt.startingCoins)
|
||||
err := suite.EvmBankKeeper.ConvertOneUkavaToAkavaIfNeeded(suite.Ctx, suite.Addrs[0], akavaNeeded)
|
||||
moduleKava := suite.BankKeeper.GetBalance(suite.Ctx, suite.AccountKeeper.GetModuleAddress(types.ModuleName), "ukava")
|
||||
if tt.success {
|
||||
suite.Require().NoError(err)
|
||||
if tt.startingCoins.AmountOf("akava").LT(akavaNeeded) {
|
||||
suite.Require().Equal(sdk.OneInt(), moduleKava.Amount)
|
||||
}
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Equal(sdk.ZeroInt(), moduleKava.Amount)
|
||||
}
|
||||
|
||||
akava := suite.Keeper.GetBalance(suite.Ctx, suite.Addrs[0])
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("akava"), akava)
|
||||
ukava := suite.BankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "ukava")
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("ukava"), ukava.Amount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestConvertAkavaToUkava() {
|
||||
tests := []struct {
|
||||
name string
|
||||
startingCoins sdk.Coins
|
||||
expectedCoins sdk.Coins
|
||||
}{
|
||||
{
|
||||
"not enough ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 100), sdk.NewInt64Coin("ukava", 0)),
|
||||
},
|
||||
{
|
||||
"converts akava for 1 ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 10), sdk.NewInt64Coin("akava", 1_000_000_000_003)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 11), sdk.NewInt64Coin("akava", 3)),
|
||||
},
|
||||
{
|
||||
"converts more than 1 ukava of akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 10), sdk.NewInt64Coin("akava", 8_000_000_000_123)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 18), sdk.NewInt64Coin("akava", 123)),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
suite.SetupTest()
|
||||
|
||||
err := suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin("ukava", 10)))
|
||||
suite.Require().NoError(err)
|
||||
suite.FundAccountWithKava(suite.Addrs[0], tt.startingCoins)
|
||||
err = suite.EvmBankKeeper.ConvertAkavaToUkava(suite.Ctx, suite.Addrs[0])
|
||||
suite.Require().NoError(err)
|
||||
akava := suite.Keeper.GetBalance(suite.Ctx, suite.Addrs[0])
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("akava"), akava)
|
||||
ukava := suite.BankKeeper.GetBalance(suite.Ctx, suite.Addrs[0], "ukava")
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("ukava"), ukava.Amount)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *evmBankKeeperTestSuite) TestSplitAkavaCoins() {
|
||||
tests := []struct {
|
||||
name string
|
||||
coins sdk.Coins
|
||||
expectedCoins sdk.Coins
|
||||
shouldErr bool
|
||||
}{
|
||||
{
|
||||
"invalid coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 500)),
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"empty coins",
|
||||
sdk.NewCoins(),
|
||||
sdk.NewCoins(),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"ukava & akava coins",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 8_000_000_000_123)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 8), sdk.NewInt64Coin("akava", 123)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"only akava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 10_123)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 10_123)),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"only ukava",
|
||||
sdk.NewCoins(sdk.NewInt64Coin("akava", 5_000_000_000_000)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("ukava", 5)),
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
suite.Run(tt.name, func() {
|
||||
ukava, akava, err := keeper.SplitAkavaCoins(tt.coins)
|
||||
if tt.shouldErr {
|
||||
suite.Require().Error(err)
|
||||
} else {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("ukava"), ukava.Amount)
|
||||
suite.Require().Equal(tt.expectedCoins.AmountOf("akava"), akava)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvmBankKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(evmBankKeeperTestSuite))
|
||||
}
|
@ -11,8 +11,6 @@ import (
|
||||
|
||||
// RegisterInvariants registers the swap module invariants
|
||||
func RegisterInvariants(ir sdk.InvariantRegistry, bankK types.BankKeeper, k Keeper) {
|
||||
ir.RegisterRoute(types.ModuleName, "fully-backed", FullyBackedInvariant(bankK, k))
|
||||
ir.RegisterRoute(types.ModuleName, "small-balances", SmallBalancesInvariant(bankK, k))
|
||||
ir.RegisterRoute(types.ModuleName, "cosmos-coins-fully-backed", CosmosCoinsFullyBackedInvariant(bankK, k))
|
||||
// Disable this invariant due to some issues with it requiring some staking params to be set in genesis.
|
||||
// ir.RegisterRoute(types.ModuleName, "backed-conversion-coins", BackedCoinsInvariant(bankK, k))
|
||||
@ -21,57 +19,11 @@ func RegisterInvariants(ir sdk.InvariantRegistry, bankK types.BankKeeper, k Keep
|
||||
// AllInvariants runs all invariants of the swap module
|
||||
func AllInvariants(bankK types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
if res, stop := FullyBackedInvariant(bankK, k)(ctx); stop {
|
||||
return res, stop
|
||||
}
|
||||
if res, stop := BackedCoinsInvariant(bankK, k)(ctx); stop {
|
||||
return res, stop
|
||||
}
|
||||
if res, stop := CosmosCoinsFullyBackedInvariant(bankK, k)(ctx); stop {
|
||||
return res, stop
|
||||
}
|
||||
return SmallBalancesInvariant(bankK, k)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// FullyBackedInvariant ensures all minor balances are backed by the coins in the module account.
|
||||
//
|
||||
// The module balance can be greater than the sum of all minor balances. This can happen in rare cases
|
||||
// where the evm module burns tokens.
|
||||
func FullyBackedInvariant(bankK types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
broken := false
|
||||
message := sdk.FormatInvariant(types.ModuleName, "fully backed broken", "sum of minor balances greater than module account")
|
||||
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
totalMinorBalances := sdk.ZeroInt()
|
||||
k.IterateAllAccounts(ctx, func(acc types.Account) bool {
|
||||
totalMinorBalances = totalMinorBalances.Add(acc.Balance)
|
||||
return false
|
||||
})
|
||||
|
||||
bankAddr := authtypes.NewModuleAddress(types.ModuleName)
|
||||
bankBalance := bankK.GetBalance(ctx, bankAddr, CosmosDenom).Amount.Mul(ConversionMultiplier)
|
||||
|
||||
broken = totalMinorBalances.GT(bankBalance)
|
||||
|
||||
return message, broken
|
||||
}
|
||||
}
|
||||
|
||||
// SmallBalancesInvariant ensures all minor balances are less than the overflow amount, beyond this they should be converted to the major denom.
|
||||
func SmallBalancesInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant {
|
||||
broken := false
|
||||
message := sdk.FormatInvariant(types.ModuleName, "small balances broken", "minor balances not all less than overflow")
|
||||
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
k.IterateAllAccounts(ctx, func(account types.Account) bool {
|
||||
if account.Balance.GTE(ConversionMultiplier) {
|
||||
broken = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return message, broken
|
||||
return CosmosCoinsFullyBackedInvariant(bankK, k)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ func (suite *invariantTestSuite) SetupValidState() {
|
||||
for i := 0; i < 4; i++ {
|
||||
suite.Keeper.SetAccount(suite.Ctx, *types.NewAccount(
|
||||
suite.Addrs[i],
|
||||
keeper.ConversionMultiplier.QuoRaw(2),
|
||||
sdkmath.NewInt(10000000000),
|
||||
))
|
||||
}
|
||||
suite.FundModuleAccountWithKava(
|
||||
@ -131,45 +131,6 @@ func (suite *invariantTestSuite) runInvariant(route string, invariant func(bankK
|
||||
return dMessage, dBroken
|
||||
}
|
||||
|
||||
func (suite *invariantTestSuite) TestFullyBackedInvariant() {
|
||||
// default state is valid
|
||||
_, broken := suite.runInvariant("fully-backed", keeper.FullyBackedInvariant)
|
||||
suite.Equal(false, broken)
|
||||
|
||||
suite.SetupValidState()
|
||||
_, broken = suite.runInvariant("fully-backed", keeper.FullyBackedInvariant)
|
||||
suite.Equal(false, broken)
|
||||
|
||||
// break invariant by increasing total minor balances above module balance
|
||||
suite.Keeper.AddBalance(suite.Ctx, suite.Addrs[0], sdk.OneInt())
|
||||
|
||||
message, broken := suite.runInvariant("fully-backed", keeper.FullyBackedInvariant)
|
||||
suite.Equal("evmutil: fully backed broken invariant\nsum of minor balances greater than module account\n", message)
|
||||
suite.Equal(true, broken)
|
||||
}
|
||||
|
||||
func (suite *invariantTestSuite) TestSmallBalances() {
|
||||
// default state is valid
|
||||
_, broken := suite.runInvariant("small-balances", keeper.SmallBalancesInvariant)
|
||||
suite.Equal(false, broken)
|
||||
|
||||
suite.SetupValidState()
|
||||
_, broken = suite.runInvariant("small-balances", keeper.SmallBalancesInvariant)
|
||||
suite.Equal(false, broken)
|
||||
|
||||
// increase minor balance at least above conversion multiplier
|
||||
suite.Keeper.AddBalance(suite.Ctx, suite.Addrs[0], keeper.ConversionMultiplier)
|
||||
// add same number of ukava to avoid breaking other invariants
|
||||
amt := sdk.NewCoins(sdk.NewInt64Coin(keeper.CosmosDenom, 1))
|
||||
suite.Require().NoError(
|
||||
suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, amt),
|
||||
)
|
||||
|
||||
message, broken := suite.runInvariant("small-balances", keeper.SmallBalancesInvariant)
|
||||
suite.Equal("evmutil: small balances broken invariant\nminor balances not all less than overflow\n", message)
|
||||
suite.Equal(true, broken)
|
||||
}
|
||||
|
||||
// the cosmos-coins-fully-backed invariant depends on 1-to-1 mapping of module balance to erc20s
|
||||
// if coins can be sent directly to the module account, this assumption is broken.
|
||||
// this test verifies that coins cannot be directly sent to the module account.
|
||||
|
@ -50,7 +50,6 @@ type Suite struct {
|
||||
BankKeeper bankkeeper.Keeper
|
||||
AccountKeeper authkeeper.AccountKeeper
|
||||
Keeper keeper.Keeper
|
||||
EvmBankKeeper keeper.EvmBankKeeper
|
||||
Addrs []sdk.AccAddress
|
||||
EvmModuleAddr sdk.AccAddress
|
||||
QueryClient types.QueryClient
|
||||
@ -68,7 +67,6 @@ func (suite *Suite) SetupTest() {
|
||||
suite.BankKeeper = tApp.GetBankKeeper()
|
||||
suite.AccountKeeper = tApp.GetAccountKeeper()
|
||||
suite.Keeper = tApp.GetEvmutilKeeper()
|
||||
suite.EvmBankKeeper = keeper.NewEvmBankKeeper(tApp.GetEvmutilKeeper(), suite.BankKeeper, suite.AccountKeeper)
|
||||
suite.EvmModuleAddr = suite.AccountKeeper.GetModuleAddress(evmtypes.ModuleName)
|
||||
|
||||
// test evm user keys that have no minting permissions
|
||||
|
@ -19,7 +19,11 @@ type AccountKeeper interface {
|
||||
|
||||
// BankKeeper defines the expected bank keeper interface
|
||||
type BankKeeper interface {
|
||||
evmtypes.BankKeeper
|
||||
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
||||
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
||||
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
|
||||
MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
|
||||
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
|
||||
|
||||
GetSupply(ctx sdk.Context, denom string) sdk.Coin
|
||||
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
|
||||
|
Loading…
Reference in New Issue
Block a user