mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 09:17:28 +00:00 
			
		
		
		
	feat: Add upgrade handler, fractional balances & reserve transfer (#1966)
Add upgrade handler Migrates from x/evmutil to x/precisebank: - Fractional balances - Reserve funds - Mints or burns coins to ensure fractional balances are fully backed. Initialize remainder if necessary to ensure valid state. E2E test with fixed kvtool
This commit is contained in:
		
							parent
							
								
									65d091d458
								
							
						
					
					
						commit
						493ce0516f
					
				
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							@ -348,7 +348,7 @@ start-remote-sims:
 | 
			
		||||
 | 
			
		||||
update-kvtool:
 | 
			
		||||
	git submodule init || true
 | 
			
		||||
	git submodule update
 | 
			
		||||
	git submodule update --remote
 | 
			
		||||
	cd tests/e2e/kvtool && make install
 | 
			
		||||
 | 
			
		||||
.PHONY: all build-linux install build test test-cli test-all test-rest test-basic test-fuzz start-remote-sims
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										273
									
								
								app/upgrades.go
									
									
									
									
									
								
							
							
						
						
									
										273
									
								
								app/upgrades.go
									
									
									
									
									
								
							@ -1,3 +1,274 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
func (app App) RegisterUpgradeHandlers() {}
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
 | 
			
		||||
	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/module"
 | 
			
		||||
	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
 | 
			
		||||
	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
 | 
			
		||||
	evmutilkeeper "github.com/kava-labs/kava/x/evmutil/keeper"
 | 
			
		||||
	evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
 | 
			
		||||
	precisebankkeeper "github.com/kava-labs/kava/x/precisebank/keeper"
 | 
			
		||||
	precisebanktypes "github.com/kava-labs/kava/x/precisebank/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	UpgradeName_Mainnet = "v0.27.0"
 | 
			
		||||
	UpgradeName_Testnet = "v0.27.0-alpha.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterUpgradeHandlers registers the upgrade handlers for the app.
 | 
			
		||||
func (app App) RegisterUpgradeHandlers() {
 | 
			
		||||
	app.upgradeKeeper.SetUpgradeHandler(
 | 
			
		||||
		UpgradeName_Mainnet,
 | 
			
		||||
		upgradeHandler(app, UpgradeName_Mainnet),
 | 
			
		||||
	)
 | 
			
		||||
	app.upgradeKeeper.SetUpgradeHandler(
 | 
			
		||||
		UpgradeName_Testnet,
 | 
			
		||||
		upgradeHandler(app, UpgradeName_Testnet),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	upgradeInfo, err := app.upgradeKeeper.ReadUpgradeInfoFromDisk()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	doUpgrade := upgradeInfo.Name == UpgradeName_Mainnet ||
 | 
			
		||||
		upgradeInfo.Name == UpgradeName_Testnet
 | 
			
		||||
 | 
			
		||||
	if doUpgrade && !app.upgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
 | 
			
		||||
		storeUpgrades := storetypes.StoreUpgrades{
 | 
			
		||||
			Added: []string{
 | 
			
		||||
				precisebanktypes.ModuleName,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// configure store loader that checks if version == upgradeHeight and applies store upgrades
 | 
			
		||||
		app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// upgradeHandler returns an UpgradeHandler for the given upgrade parameters.
 | 
			
		||||
func upgradeHandler(
 | 
			
		||||
	app App,
 | 
			
		||||
	name string,
 | 
			
		||||
) upgradetypes.UpgradeHandler {
 | 
			
		||||
	return func(
 | 
			
		||||
		ctx sdk.Context,
 | 
			
		||||
		plan upgradetypes.Plan,
 | 
			
		||||
		fromVM module.VersionMap,
 | 
			
		||||
	) (module.VersionMap, error) {
 | 
			
		||||
		logger := app.Logger()
 | 
			
		||||
		logger.Info(fmt.Sprintf("running %s upgrade handler", name))
 | 
			
		||||
 | 
			
		||||
		// Run migrations for all modules and return new consensus version map.
 | 
			
		||||
		versionMap, err := app.mm.RunMigrations(ctx, app.configurator, fromVM)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Info("completed store migrations")
 | 
			
		||||
 | 
			
		||||
		// Migration of fractional balances from x/evmutil to x/precisebank
 | 
			
		||||
		if err := MigrateEvmutilToPrecisebank(
 | 
			
		||||
			ctx,
 | 
			
		||||
			app.accountKeeper,
 | 
			
		||||
			app.bankKeeper,
 | 
			
		||||
			app.evmutilKeeper,
 | 
			
		||||
			app.precisebankKeeper,
 | 
			
		||||
		); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Info("completed x/evmutil to x/precisebank migration")
 | 
			
		||||
 | 
			
		||||
		return versionMap, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MigrateEvmutilToPrecisebank migrates all required state from x/evmutil to
 | 
			
		||||
// x/precisebank and ensures the resulting state is correct.
 | 
			
		||||
// This migrates the following state:
 | 
			
		||||
// - Fractional balances
 | 
			
		||||
// - Fractional balance reserve
 | 
			
		||||
// Initializes the following state in x/precisebank:
 | 
			
		||||
// - Remainder amount
 | 
			
		||||
func MigrateEvmutilToPrecisebank(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	accountKeeper evmutiltypes.AccountKeeper,
 | 
			
		||||
	bankKeeper bankkeeper.Keeper,
 | 
			
		||||
	evmutilKeeper evmutilkeeper.Keeper,
 | 
			
		||||
	precisebankKeeper precisebankkeeper.Keeper,
 | 
			
		||||
) error {
 | 
			
		||||
	logger := ctx.Logger()
 | 
			
		||||
 | 
			
		||||
	aggregateSum, err := TransferFractionalBalances(
 | 
			
		||||
		ctx,
 | 
			
		||||
		evmutilKeeper,
 | 
			
		||||
		precisebankKeeper,
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("fractional balances transfer: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	logger.Info(
 | 
			
		||||
		"fractional balances transferred from x/evmutil to x/precisebank",
 | 
			
		||||
		"aggregate sum", aggregateSum,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	remainder := InitializeRemainder(ctx, precisebankKeeper, aggregateSum)
 | 
			
		||||
	logger.Info("remainder amount initialized in x/precisebank", "remainder", remainder)
 | 
			
		||||
 | 
			
		||||
	// Migrate fractional balances, reserve, and ensure reserve fully backs all
 | 
			
		||||
	// fractional balances.
 | 
			
		||||
	if err := TransferFractionalBalanceReserve(
 | 
			
		||||
		ctx,
 | 
			
		||||
		accountKeeper,
 | 
			
		||||
		bankKeeper,
 | 
			
		||||
		precisebankKeeper,
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return fmt.Errorf("reserve transfer: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TransferFractionalBalances migrates fractional balances from x/evmutil to
 | 
			
		||||
// x/precisebank. It sets the fractional balance in x/precisebank and deletes
 | 
			
		||||
// the account from x/evmutil. Returns the aggregate sum of all fractional
 | 
			
		||||
// balances.
 | 
			
		||||
func TransferFractionalBalances(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	evmutilKeeper evmutilkeeper.Keeper,
 | 
			
		||||
	precisebankKeeper precisebankkeeper.Keeper,
 | 
			
		||||
) (sdkmath.Int, error) {
 | 
			
		||||
	aggregateSum := sdkmath.ZeroInt()
 | 
			
		||||
 | 
			
		||||
	var iterErr error
 | 
			
		||||
 | 
			
		||||
	evmutilKeeper.IterateAllAccounts(ctx, func(acc evmutiltypes.Account) bool {
 | 
			
		||||
		// Set account balance in x/precisebank
 | 
			
		||||
		precisebankKeeper.SetFractionalBalance(ctx, acc.Address, acc.Balance)
 | 
			
		||||
 | 
			
		||||
		// Delete account from x/evmutil
 | 
			
		||||
		iterErr := evmutilKeeper.SetAccount(ctx, evmutiltypes.Account{
 | 
			
		||||
			Address: acc.Address,
 | 
			
		||||
			// Set balance to 0 to delete it
 | 
			
		||||
			Balance: sdkmath.ZeroInt(),
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		// Halt iteration if there was an error
 | 
			
		||||
		if iterErr != nil {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Aggregate sum of all fractional balances
 | 
			
		||||
		aggregateSum = aggregateSum.Add(acc.Balance)
 | 
			
		||||
 | 
			
		||||
		// Continue iterating
 | 
			
		||||
		return false
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return aggregateSum, iterErr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InitializeRemainder initializes the remainder amount in x/precisebank. It
 | 
			
		||||
// calculates the remainder amount that is needed to ensure that the sum of all
 | 
			
		||||
// fractional balances is a multiple of the conversion factor. The remainder
 | 
			
		||||
// amount is stored in the store and returned.
 | 
			
		||||
func InitializeRemainder(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	precisebankKeeper precisebankkeeper.Keeper,
 | 
			
		||||
	aggregateSum sdkmath.Int,
 | 
			
		||||
) sdkmath.Int {
 | 
			
		||||
	// Extra fractional coins that exceed the conversion factor.
 | 
			
		||||
	// This extra + remainder should equal the conversion factor to ensure
 | 
			
		||||
	// (sum(fBalances) + remainder) % conversionFactor = 0
 | 
			
		||||
	extraFractionalAmount := aggregateSum.Mod(precisebanktypes.ConversionFactor())
 | 
			
		||||
	remainder := precisebanktypes.ConversionFactor().
 | 
			
		||||
		Sub(extraFractionalAmount).
 | 
			
		||||
		// Mod conversion factor to ensure remainder is valid.
 | 
			
		||||
		// If extraFractionalAmount is a multiple of conversion factor, the
 | 
			
		||||
		// remainder is 0.
 | 
			
		||||
		Mod(precisebanktypes.ConversionFactor())
 | 
			
		||||
 | 
			
		||||
	// Panics if the remainder is invalid. In a correct chain state and only
 | 
			
		||||
	// mint/burns due to transfers, this would be 0.
 | 
			
		||||
	precisebankKeeper.SetRemainderAmount(ctx, remainder)
 | 
			
		||||
 | 
			
		||||
	return remainder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TransferFractionalBalanceReserve migrates the fractional balance reserve from
 | 
			
		||||
// x/evmutil to x/precisebank. It transfers the reserve balance from x/evmutil
 | 
			
		||||
// to x/precisebank and ensures that the reserve fully backs all fractional
 | 
			
		||||
// balances. It mints or burns coins to back the fractional balances exactly.
 | 
			
		||||
func TransferFractionalBalanceReserve(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	accountKeeper evmutiltypes.AccountKeeper,
 | 
			
		||||
	bankKeeper bankkeeper.Keeper,
 | 
			
		||||
	precisebankKeeper precisebankkeeper.Keeper,
 | 
			
		||||
) error {
 | 
			
		||||
	logger := ctx.Logger()
 | 
			
		||||
 | 
			
		||||
	// Transfer x/evmutil reserve to x/precisebank.
 | 
			
		||||
	evmutilAddr := accountKeeper.GetModuleAddress(evmutiltypes.ModuleName)
 | 
			
		||||
	reserveBalance := bankKeeper.GetBalance(ctx, evmutilAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
 | 
			
		||||
	if err := bankKeeper.SendCoinsFromModuleToModule(
 | 
			
		||||
		ctx,
 | 
			
		||||
		evmutiltypes.ModuleName,     // from x/evmutil
 | 
			
		||||
		precisebanktypes.ModuleName, // to x/precisebank
 | 
			
		||||
		sdk.NewCoins(reserveBalance),
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to transfer reserve from x/evmutil to x/precisebank: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Info(fmt.Sprintf("transferred reserve balance: %s", reserveBalance))
 | 
			
		||||
 | 
			
		||||
	// Ensure x/precisebank reserve fully backs all fractional balances.
 | 
			
		||||
	totalFractionalBalances := precisebankKeeper.GetTotalSumFractionalBalances(ctx)
 | 
			
		||||
 | 
			
		||||
	// Does NOT ensure state is correct, total fractional balances should be a
 | 
			
		||||
	// multiple of conversion factor but is not guaranteed due to the remainder.
 | 
			
		||||
	// Remainder initialization is handled by InitializeRemainder.
 | 
			
		||||
 | 
			
		||||
	// Determine how much the reserve is off by, e.g. unbacked amount
 | 
			
		||||
	expectedReserveBalance := totalFractionalBalances.Quo(precisebanktypes.ConversionFactor())
 | 
			
		||||
 | 
			
		||||
	// If there is a remainder (totalFractionalBalances % conversionFactor != 0),
 | 
			
		||||
	// then expectedReserveBalance is rounded up to the nearest integer.
 | 
			
		||||
	if totalFractionalBalances.Mod(precisebanktypes.ConversionFactor()).IsPositive() {
 | 
			
		||||
		expectedReserveBalance = expectedReserveBalance.Add(sdkmath.OneInt())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unbackedAmount := expectedReserveBalance.Sub(reserveBalance.Amount)
 | 
			
		||||
	logger.Info(fmt.Sprintf("total account fractional balances: %s", totalFractionalBalances))
 | 
			
		||||
 | 
			
		||||
	// Three possible cases:
 | 
			
		||||
	// 1. Reserve is not enough, mint coins to back the fractional balances
 | 
			
		||||
	// 2. Reserve is too much, burn coins to back the fractional balances exactly
 | 
			
		||||
	// 3. Reserve is exactly enough, no action needed
 | 
			
		||||
	if unbackedAmount.IsPositive() {
 | 
			
		||||
		coins := sdk.NewCoins(sdk.NewCoin(precisebanktypes.IntegerCoinDenom, unbackedAmount))
 | 
			
		||||
		if err := bankKeeper.MintCoins(ctx, precisebanktypes.ModuleName, coins); err != nil {
 | 
			
		||||
			return fmt.Errorf("failed to mint extra reserve coins: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Info(fmt.Sprintf("unbacked amount minted to reserve: %s", unbackedAmount))
 | 
			
		||||
	} else if unbackedAmount.IsNegative() {
 | 
			
		||||
		coins := sdk.NewCoins(sdk.NewCoin(precisebanktypes.IntegerCoinDenom, unbackedAmount.Neg()))
 | 
			
		||||
		if err := bankKeeper.BurnCoins(ctx, precisebanktypes.ModuleName, coins); err != nil {
 | 
			
		||||
			return fmt.Errorf("failed to burn extra reserve coins: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Info(fmt.Sprintf("extra reserve amount burned: %s", unbackedAmount.Neg()))
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.Info("reserve exactly backs fractional balances, no mint/burn needed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										434
									
								
								app/upgrades_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								app/upgrades_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,434 @@
 | 
			
		||||
package app_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
 | 
			
		||||
	precisebankkeeper "github.com/kava-labs/kava/x/precisebank/keeper"
 | 
			
		||||
	precisebanktypes "github.com/kava-labs/kava/x/precisebank/types"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestMigrateEvmutilToPrecisebank(t *testing.T) {
 | 
			
		||||
	// Full test case with all components together
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name               string
 | 
			
		||||
		initialReserve     sdkmath.Int
 | 
			
		||||
		fractionalBalances []sdkmath.Int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"no fractional balances",
 | 
			
		||||
			sdkmath.NewInt(0),
 | 
			
		||||
			[]sdkmath.Int{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"sufficient reserve, 0 remainder",
 | 
			
		||||
			// Accounts adding up to 2 int units, same as reserve
 | 
			
		||||
			sdkmath.NewInt(2),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"insufficient reserve, 0 remainder",
 | 
			
		||||
			// Accounts adding up to 2 int units, but only 1 int unit in reserve
 | 
			
		||||
			sdkmath.NewInt(1),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"excess reserve, 0 remainder",
 | 
			
		||||
			// Accounts adding up to 2 int units, but 3 int unit in reserve
 | 
			
		||||
			sdkmath.NewInt(3),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"sufficient reserve, non-zero remainder",
 | 
			
		||||
			// Accounts adding up to 1.5 int units, same as reserve
 | 
			
		||||
			sdkmath.NewInt(2),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"insufficient reserve, non-zero remainder",
 | 
			
		||||
			// Accounts adding up to 1.5 int units, less than reserve,
 | 
			
		||||
			// Reserve should be 2 and remainder 0.5
 | 
			
		||||
			sdkmath.NewInt(1),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"excess reserve, non-zero remainder",
 | 
			
		||||
			// Accounts adding up to 1.5 int units, 3 int units in reserve
 | 
			
		||||
			sdkmath.NewInt(3),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tApp := app.NewTestApp()
 | 
			
		||||
			tApp.InitializeFromGenesisStates()
 | 
			
		||||
			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
 | 
			
		||||
 | 
			
		||||
			ak := tApp.GetAccountKeeper()
 | 
			
		||||
			bk := tApp.GetBankKeeper()
 | 
			
		||||
			evmuk := tApp.GetEvmutilKeeper()
 | 
			
		||||
			pbk := tApp.GetPrecisebankKeeper()
 | 
			
		||||
 | 
			
		||||
			reserveCoin := sdk.NewCoin(precisebanktypes.IntegerCoinDenom, tt.initialReserve)
 | 
			
		||||
			err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(reserveCoin))
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
 | 
			
		||||
			newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
 | 
			
		||||
 | 
			
		||||
			// Double check balances
 | 
			
		||||
			oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
 | 
			
		||||
			require.Equal(t, tt.initialReserve, oldReserveBalance.Amount, "initial x/evmutil reserve balance")
 | 
			
		||||
			require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
 | 
			
		||||
 | 
			
		||||
			// Set accounts
 | 
			
		||||
			for i, balance := range tt.fractionalBalances {
 | 
			
		||||
				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
 | 
			
		||||
 | 
			
		||||
				err := evmuk.SetBalance(ctx, addr, balance)
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Run full x/evmutil -> x/precisebank migration
 | 
			
		||||
			err = app.MigrateEvmutilToPrecisebank(
 | 
			
		||||
				ctx,
 | 
			
		||||
				ak,
 | 
			
		||||
				bk,
 | 
			
		||||
				evmuk,
 | 
			
		||||
				pbk,
 | 
			
		||||
			)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			// Check old reserve is empty
 | 
			
		||||
			oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
 | 
			
		||||
 | 
			
		||||
			// Check new reserve fully backs fractional balances
 | 
			
		||||
			newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
 | 
			
		||||
			remainder := pbk.GetRemainderAmount(ctx)
 | 
			
		||||
 | 
			
		||||
			expectedReserveBal := fractionalBalanceTotal.Add(remainder)
 | 
			
		||||
			require.Equal(
 | 
			
		||||
				t,
 | 
			
		||||
				expectedReserveBal,
 | 
			
		||||
				newReserveBalanceAfter.Amount.Mul(precisebanktypes.ConversionFactor()),
 | 
			
		||||
				"new reserve should equal total fractional balances",
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			// Check balances are deleted in evmutil and migrated to precisebank
 | 
			
		||||
			for i := range tt.fractionalBalances {
 | 
			
		||||
				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
 | 
			
		||||
				acc := evmuk.GetAccount(ctx, addr)
 | 
			
		||||
				require.Nil(t, acc, "account should be deleted")
 | 
			
		||||
 | 
			
		||||
				balance := pbk.GetFractionalBalance(ctx, addr)
 | 
			
		||||
				require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Checks balances valid and remainder
 | 
			
		||||
			res, stop := precisebankkeeper.AllInvariants(pbk)(ctx)
 | 
			
		||||
			require.Falsef(t, stop, "invariants should pass: %s", res)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTransferFractionalBalances(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name               string
 | 
			
		||||
		fractionalBalances []sdkmath.Int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"no fractional balances",
 | 
			
		||||
			[]sdkmath.Int{},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"balanced fractional balances",
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 4 accounts
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"unbalanced balances",
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 3 accounts
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tApp := app.NewTestApp()
 | 
			
		||||
			tApp.InitializeFromGenesisStates()
 | 
			
		||||
			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
 | 
			
		||||
 | 
			
		||||
			evmutilk := tApp.GetEvmutilKeeper()
 | 
			
		||||
			pbk := tApp.GetPrecisebankKeeper()
 | 
			
		||||
 | 
			
		||||
			for i, balance := range tt.fractionalBalances {
 | 
			
		||||
				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
 | 
			
		||||
 | 
			
		||||
				err := evmutilk.SetBalance(ctx, addr, balance)
 | 
			
		||||
				require.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Run balance transfer
 | 
			
		||||
			aggregateSum, err := app.TransferFractionalBalances(
 | 
			
		||||
				ctx,
 | 
			
		||||
				evmutilk,
 | 
			
		||||
				pbk,
 | 
			
		||||
			)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			// Check balances are deleted in evmutil and migrated to precisebank
 | 
			
		||||
			sum := sdkmath.ZeroInt()
 | 
			
		||||
			for i := range tt.fractionalBalances {
 | 
			
		||||
				sum = sum.Add(tt.fractionalBalances[i])
 | 
			
		||||
 | 
			
		||||
				addr := sdk.AccAddress([]byte(strconv.Itoa(i)))
 | 
			
		||||
				acc := evmutilk.GetAccount(ctx, addr)
 | 
			
		||||
				require.Nil(t, acc, "account should be deleted")
 | 
			
		||||
 | 
			
		||||
				balance := pbk.GetFractionalBalance(ctx, addr)
 | 
			
		||||
				require.Equal(t, tt.fractionalBalances[i], balance, "balance should be migrated")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			require.Equal(t, sum, aggregateSum, "aggregate sum should be correct")
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInitializeRemainder(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name             string
 | 
			
		||||
		giveAggregateSum sdkmath.Int
 | 
			
		||||
		wantRemainder    sdkmath.Int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"0 remainder, 1ukava",
 | 
			
		||||
			precisebanktypes.ConversionFactor(),
 | 
			
		||||
			sdkmath.NewInt(0),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"0 remainder, multiple ukava",
 | 
			
		||||
			precisebanktypes.ConversionFactor().MulRaw(5),
 | 
			
		||||
			sdkmath.NewInt(0),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"non-zero remainder, min",
 | 
			
		||||
			precisebanktypes.ConversionFactor().SubRaw(1),
 | 
			
		||||
			sdkmath.NewInt(1),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"non-zero remainder, max",
 | 
			
		||||
			sdkmath.NewInt(1),
 | 
			
		||||
			precisebanktypes.ConversionFactor().SubRaw(1),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"non-zero remainder, half",
 | 
			
		||||
			precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tApp := app.NewTestApp()
 | 
			
		||||
			tApp.InitializeFromGenesisStates()
 | 
			
		||||
 | 
			
		||||
			pbk := tApp.GetPrecisebankKeeper()
 | 
			
		||||
 | 
			
		||||
			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
 | 
			
		||||
 | 
			
		||||
			remainder := app.InitializeRemainder(
 | 
			
		||||
				ctx,
 | 
			
		||||
				tApp.GetPrecisebankKeeper(),
 | 
			
		||||
				tt.giveAggregateSum,
 | 
			
		||||
			)
 | 
			
		||||
			require.Equal(t, tt.wantRemainder, remainder)
 | 
			
		||||
 | 
			
		||||
			// Check actual state
 | 
			
		||||
			remainderAfter := pbk.GetRemainderAmount(ctx)
 | 
			
		||||
			require.Equal(t, tt.wantRemainder, remainderAfter)
 | 
			
		||||
 | 
			
		||||
			// Not checking invariants here since it requires actual balance state
 | 
			
		||||
			aggregateSumWithRemainder := tt.giveAggregateSum.Add(remainder)
 | 
			
		||||
			require.True(
 | 
			
		||||
				t,
 | 
			
		||||
				aggregateSumWithRemainder.
 | 
			
		||||
					Mod(precisebanktypes.ConversionFactor()).
 | 
			
		||||
					IsZero(),
 | 
			
		||||
				"remainder + aggregate sum should be a multiple of the conversion factor",
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestTransferFractionalBalanceReserve(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name               string
 | 
			
		||||
		initialReserve     sdk.Coin
 | 
			
		||||
		fractionalBalances []sdkmath.Int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"balanced reserve, no remainder",
 | 
			
		||||
			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 2 accounts
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"insufficient reserve",
 | 
			
		||||
			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 4 accounts, total 2 int units
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"extra reserve funds",
 | 
			
		||||
			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(2)),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 2 accounts, total 1 int units
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"insufficient reserve, with remainder",
 | 
			
		||||
			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(1)),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 5 accounts, total 2.5 int units
 | 
			
		||||
				// Expected 3 int units in reserve, 0.5 remainder
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"extra reserve funds, with remainder",
 | 
			
		||||
			sdk.NewCoin(precisebanktypes.IntegerCoinDenom, sdk.NewInt(3)),
 | 
			
		||||
			[]sdkmath.Int{
 | 
			
		||||
				// 3 accounts, total 1.5 int units.
 | 
			
		||||
				// Expected 2 int units in reserve, 0.5 remainder
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
				precisebanktypes.ConversionFactor().QuoRaw(2),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			tApp := app.NewTestApp()
 | 
			
		||||
			tApp.InitializeFromGenesisStates()
 | 
			
		||||
			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: time.Now()})
 | 
			
		||||
 | 
			
		||||
			bk := tApp.GetBankKeeper()
 | 
			
		||||
			pbk := tApp.GetPrecisebankKeeper()
 | 
			
		||||
			err := bk.MintCoins(ctx, evmutiltypes.ModuleName, sdk.NewCoins(tt.initialReserve))
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			oldReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(evmutiltypes.ModuleName)
 | 
			
		||||
			newReserveAddr := tApp.GetAccountKeeper().GetModuleAddress(precisebanktypes.ModuleName)
 | 
			
		||||
 | 
			
		||||
			// Double check balances
 | 
			
		||||
			oldReserveBalance := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			newReserveBalance := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
 | 
			
		||||
			require.Equal(t, tt.initialReserve, oldReserveBalance)
 | 
			
		||||
			require.True(t, newReserveBalance.IsZero(), "empty initial new reserve")
 | 
			
		||||
 | 
			
		||||
			for i, balance := range tt.fractionalBalances {
 | 
			
		||||
				addr := sdk.AccAddress([]byte{byte(i)})
 | 
			
		||||
 | 
			
		||||
				require.NotPanics(t, func() {
 | 
			
		||||
					pbk.SetFractionalBalance(ctx, addr, balance)
 | 
			
		||||
				}, "given fractional balances should be valid")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Run reserve migration
 | 
			
		||||
			err = app.TransferFractionalBalanceReserve(
 | 
			
		||||
				ctx,
 | 
			
		||||
				tApp.GetAccountKeeper(),
 | 
			
		||||
				bk,
 | 
			
		||||
				tApp.GetPrecisebankKeeper(),
 | 
			
		||||
			)
 | 
			
		||||
			require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
			// Check old reserve is empty
 | 
			
		||||
			oldReserveBalanceAfter := bk.GetBalance(ctx, oldReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			require.True(t, oldReserveBalanceAfter.IsZero(), "old reserve should be empty")
 | 
			
		||||
 | 
			
		||||
			// Check new reserve fully backs fractional balances
 | 
			
		||||
			newReserveBalanceAfter := bk.GetBalance(ctx, newReserveAddr, precisebanktypes.IntegerCoinDenom)
 | 
			
		||||
			fractionalBalanceTotal := pbk.GetTotalSumFractionalBalances(ctx)
 | 
			
		||||
 | 
			
		||||
			expectedReserveBal := fractionalBalanceTotal.
 | 
			
		||||
				Quo(precisebanktypes.ConversionFactor())
 | 
			
		||||
 | 
			
		||||
			// Check if theres a remainder
 | 
			
		||||
			if fractionalBalanceTotal.Mod(precisebanktypes.ConversionFactor()).IsPositive() {
 | 
			
		||||
				expectedReserveBal = expectedReserveBal.Add(sdkmath.OneInt())
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			require.Equal(
 | 
			
		||||
				t,
 | 
			
		||||
				expectedReserveBal,
 | 
			
		||||
				newReserveBalanceAfter.Amount,
 | 
			
		||||
				"new reserve should equal total fractional balances + remainder",
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -19,14 +19,14 @@ E2E_SKIP_SHUTDOWN=false
 | 
			
		||||
 | 
			
		||||
# The following variables should be defined to run an upgrade.
 | 
			
		||||
# E2E_INCLUDE_AUTOMATED_UPGRADE when true enables the automated upgrade & corresponding tests in the suite.
 | 
			
		||||
E2E_INCLUDE_AUTOMATED_UPGRADE=false
 | 
			
		||||
E2E_INCLUDE_AUTOMATED_UPGRADE=true
 | 
			
		||||
# E2E_KAVA_UPGRADE_NAME is the name of the upgrade that must be in the current local image.
 | 
			
		||||
E2E_KAVA_UPGRADE_NAME=
 | 
			
		||||
E2E_KAVA_UPGRADE_NAME=v0.27.0
 | 
			
		||||
# E2E_KAVA_UPGRADE_HEIGHT is the height at which the upgrade will be applied.
 | 
			
		||||
# If IBC tests are enabled this should be >30. Otherwise, this should be >10.
 | 
			
		||||
E2E_KAVA_UPGRADE_HEIGHT=
 | 
			
		||||
E2E_KAVA_UPGRADE_HEIGHT=35
 | 
			
		||||
# E2E_KAVA_UPGRADE_BASE_IMAGE_TAG is the tag of the docker image the chain should upgrade from.
 | 
			
		||||
E2E_KAVA_UPGRADE_BASE_IMAGE_TAG=
 | 
			
		||||
E2E_KAVA_UPGRADE_BASE_IMAGE_TAG=v0.26.0-goleveldb
 | 
			
		||||
 | 
			
		||||
# E2E_KAVA_ERC20_ADDRESS is the address of a pre-deployed ERC20 token with the following properties:
 | 
			
		||||
# - the E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC has nonzero balance
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,102 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	precisebanktypes "github.com/kava-labs/kava/x/precisebank/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TestUpgradeHandler can be used to run tests post-upgrade. If an upgrade is enabled, all tests
 | 
			
		||||
// are run against the upgraded chain. However, this file is a good place to consolidate all
 | 
			
		||||
// acceptance tests for a given set of upgrade handlers.
 | 
			
		||||
func (suite *IntegrationTestSuite) TestUpgradeHandler() {
 | 
			
		||||
func (suite *IntegrationTestSuite) TestUpgrade_PreciseBankReserveTransfer() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
	fmt.Println("An upgrade has run!")
 | 
			
		||||
	suite.True(true)
 | 
			
		||||
 | 
			
		||||
	// Uncomment & use these contexts to compare chain state before & after the upgrade occurs.
 | 
			
		||||
	// beforeUpgradeCtx := suite.Kava.Grpc.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	// afterUpgradeCtx := suite.Kava.Grpc.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
	beforeUpgradeCtx := suite.Kava.Grpc.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := suite.Kava.Grpc.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	grpcClient := suite.Kava.Grpc
 | 
			
		||||
 | 
			
		||||
	// -----------------------------
 | 
			
		||||
	// Get initial reserve balances
 | 
			
		||||
	evmutilAddr := "kava1w9vxuke5dz6hyza2j932qgmxltnfxwl78u920k"
 | 
			
		||||
	precisebankAddr := "kava12yfe2jaupmtjruwxsec7hg7er60fhaa4uz7ffl"
 | 
			
		||||
 | 
			
		||||
	previousEvmutilBalRes, err := grpcClient.Query.Bank.Balance(beforeUpgradeCtx, &banktypes.QueryBalanceRequest{
 | 
			
		||||
		Address: evmutilAddr,
 | 
			
		||||
		Denom:   precisebanktypes.IntegerCoinDenom,
 | 
			
		||||
	})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotNil(previousEvmutilBalRes.Balance)
 | 
			
		||||
	suite.Require().True(
 | 
			
		||||
		previousEvmutilBalRes.Balance.Amount.IsPositive(),
 | 
			
		||||
		"should have reserve balance before upgrade",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	previousPrecisebankBalRes, err := grpcClient.Query.Bank.Balance(beforeUpgradeCtx, &banktypes.QueryBalanceRequest{
 | 
			
		||||
		Address: precisebankAddr,
 | 
			
		||||
		Denom:   precisebanktypes.IntegerCoinDenom,
 | 
			
		||||
	})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotNil(previousPrecisebankBalRes.Balance)
 | 
			
		||||
	suite.Require().True(
 | 
			
		||||
		previousPrecisebankBalRes.Balance.Amount.IsZero(),
 | 
			
		||||
		"should be empty before upgrade",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	suite.T().Logf("x/evmutil balances before upgrade: %s", previousEvmutilBalRes.Balance)
 | 
			
		||||
	suite.T().Logf("x/precisebank balances before upgrade: %s", previousPrecisebankBalRes.Balance)
 | 
			
		||||
 | 
			
		||||
	// -----------------------------
 | 
			
		||||
	// After upgrade
 | 
			
		||||
	// - Check reserve balance transfer
 | 
			
		||||
	// - Check reserve fully backs fractional amounts
 | 
			
		||||
	afterEvmutilBalRes, err := grpcClient.Query.Bank.Balance(afterUpgradeCtx, &banktypes.QueryBalanceRequest{
 | 
			
		||||
		Address: evmutilAddr,
 | 
			
		||||
		Denom:   precisebanktypes.IntegerCoinDenom,
 | 
			
		||||
	})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotNil(afterEvmutilBalRes.Balance)
 | 
			
		||||
	suite.Require().Truef(
 | 
			
		||||
		afterEvmutilBalRes.Balance.Amount.IsZero(),
 | 
			
		||||
		"should have transferred all reserve balance to precisebank, expected 0 but got %s",
 | 
			
		||||
		afterEvmutilBalRes.Balance,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	afterPrecisebankBalRes, err := grpcClient.Query.Bank.Balance(afterUpgradeCtx, &banktypes.QueryBalanceRequest{
 | 
			
		||||
		Address: precisebankAddr,
 | 
			
		||||
		Denom:   precisebanktypes.IntegerCoinDenom,
 | 
			
		||||
	})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotNil(afterPrecisebankBalRes.Balance)
 | 
			
		||||
	// 2 total in reserve- genesis.json has 5 accounts with fractional balances
 | 
			
		||||
	// totalling 2 integer coins
 | 
			
		||||
	suite.Require().Equal(int64(2), afterPrecisebankBalRes.Balance.Amount.Int64())
 | 
			
		||||
 | 
			
		||||
	suite.T().Logf("x/evmutil balances after upgrade: %s", afterEvmutilBalRes.Balance)
 | 
			
		||||
	suite.T().Logf("x/precisebank balances after upgrade: %s", afterPrecisebankBalRes.Balance)
 | 
			
		||||
 | 
			
		||||
	sumFractional, err := grpcClient.Query.Precisebank.TotalFractionalBalances(
 | 
			
		||||
		afterUpgradeCtx,
 | 
			
		||||
		&precisebanktypes.QueryTotalFractionalBalancesRequest{},
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	suite.Require().Equal(
 | 
			
		||||
		sumFractional.Total.Amount,
 | 
			
		||||
		afterPrecisebankBalRes.Balance.Amount.Mul(precisebanktypes.ConversionFactor()),
 | 
			
		||||
		"reserve should match exactly sum fractional balances",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Check remainder + total fractional balances = reserve balance
 | 
			
		||||
	remainderRes, err := grpcClient.Query.Precisebank.Remainder(
 | 
			
		||||
		afterUpgradeCtx,
 | 
			
		||||
		&precisebanktypes.QueryRemainderRequest{},
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	sumFractionalAndRemainder := sumFractional.Total.Add(remainderRes.Remainder)
 | 
			
		||||
	reserveBalanceExtended := afterPrecisebankBalRes.Balance.Amount.Mul(precisebanktypes.ConversionFactor())
 | 
			
		||||
 | 
			
		||||
	suite.Require().Equal(
 | 
			
		||||
		sumFractionalAndRemainder.Amount,
 | 
			
		||||
		reserveBalanceExtended,
 | 
			
		||||
		"remainder + sum(fractional balances) should be = reserve balance",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user