mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 03:27:26 +00:00 
			
		
		
		
	feat(evmutil): add CosmosCoinsFullyBackedInvariant (#1610)
* add IterateAllDeployedCosmosCoinContracts method * refactor unpacking big int from erc20 query * add QueryERC20TotalSupply method * feat(evmutil): add CosmosCoinsFullyBackedInvariant * update changelog
This commit is contained in:
		
							parent
							
								
									f4b8bf8f07
								
							
						
					
					
						commit
						528be6350e
					
				@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
 | 
			
		||||
- (evmutil) [#1604] Emit events for MsgConvertCosmosCoinToERC20: `message` & `convert_cosmos_coin_to_erc20`
 | 
			
		||||
- (evmutil) [#1605] Add query for deployed ERC20 contracts representing Cosmos coins in the EVM
 | 
			
		||||
- (evmutil) [#1609] Add MsgConvertCosmosCoinFromERC20 for converting the ERC20 back to an sdk.Coin
 | 
			
		||||
- (evmutil) [#1610] Add new invariant checking that ERC20s are fully backed by sdk.Coins
 | 
			
		||||
 | 
			
		||||
### Client Breaking
 | 
			
		||||
- (evmutil) [#1603] Renamed error `ErrConversionNotEnabled` to `ErrEVMConversionNotEnabled`
 | 
			
		||||
@ -252,6 +253,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
 | 
			
		||||
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
 | 
			
		||||
  large-scale simulations remotely using aws-batch
 | 
			
		||||
 | 
			
		||||
[#1610]: https://github.com/Kava-Labs/kava/pull/1610
 | 
			
		||||
[#1609]: https://github.com/Kava-Labs/kava/pull/1609
 | 
			
		||||
[#1605]: https://github.com/Kava-Labs/kava/pull/1605
 | 
			
		||||
[#1604]: https://github.com/Kava-Labs/kava/pull/1604
 | 
			
		||||
 | 
			
		||||
@ -1138,6 +1138,7 @@ func (app *App) loadBlockedMaccAddrs() map[string]bool {
 | 
			
		||||
		app.accountKeeper.GetModuleAddress(kavadisttypes.FundModuleAccount).String(): true,
 | 
			
		||||
		// community
 | 
			
		||||
		app.accountKeeper.GetModuleAddress(communitytypes.ModuleAccountName).String(): true,
 | 
			
		||||
		// NOTE: if adding evmutil, adjust the cosmos-coins-fully-backed-invariant accordingly.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for addr := range modAccAddrs {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,10 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	erc20BalanceOfMethod = "balanceOf"
 | 
			
		||||
	erc20BalanceOfMethod   = "balanceOf"
 | 
			
		||||
	erc20BurnMethod        = "burn"
 | 
			
		||||
	erc20MintMethod        = "mint"
 | 
			
		||||
	erc20TotalSupplyMethod = "totalSupply"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DeployTestMintableERC20Contract deploys an ERC20 contract on the EVM as the
 | 
			
		||||
@ -149,7 +152,7 @@ func (k Keeper) MintERC20(
 | 
			
		||||
		types.ERC20MintableBurnableContract.ABI,
 | 
			
		||||
		types.ModuleEVMAddress,
 | 
			
		||||
		contractAddr,
 | 
			
		||||
		"mint",
 | 
			
		||||
		erc20MintMethod,
 | 
			
		||||
		// Mint ERC20 args
 | 
			
		||||
		receiver.Address,
 | 
			
		||||
		amount,
 | 
			
		||||
@ -170,7 +173,7 @@ func (k Keeper) BurnERC20(
 | 
			
		||||
		types.ERC20KavaWrappedCosmosCoinContract.ABI,
 | 
			
		||||
		types.ModuleEVMAddress,
 | 
			
		||||
		contractAddr,
 | 
			
		||||
		"burn",
 | 
			
		||||
		erc20BurnMethod,
 | 
			
		||||
		// Burn ERC20 args
 | 
			
		||||
		initiator.Address,
 | 
			
		||||
		amount,
 | 
			
		||||
@ -179,6 +182,8 @@ func (k Keeper) BurnERC20(
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QueryERC20BalanceOf makes a contract call to the balanceOf method of the ERC20 contract to get
 | 
			
		||||
// the ERC20 balance of the given account.
 | 
			
		||||
func (k Keeper) QueryERC20BalanceOf(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	contractAddr types.InternalEVMAddress,
 | 
			
		||||
@ -197,6 +202,31 @@ func (k Keeper) QueryERC20BalanceOf(
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return unpackERC20ResToBigInt(res, erc20BalanceOfMethod)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QueryERC20TotalSupply makes a contract call to the totalSupply method of the ERC20 contract to
 | 
			
		||||
// get the total supply of the token.
 | 
			
		||||
func (k Keeper) QueryERC20TotalSupply(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	contractAddr types.InternalEVMAddress,
 | 
			
		||||
) (*big.Int, error) {
 | 
			
		||||
	res, err := k.CallEVM(
 | 
			
		||||
		ctx,
 | 
			
		||||
		types.ERC20KavaWrappedCosmosCoinContract.ABI,
 | 
			
		||||
		types.ModuleEVMAddress,
 | 
			
		||||
		contractAddr,
 | 
			
		||||
		erc20TotalSupplyMethod,
 | 
			
		||||
		// totalSupply takes no args
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return unpackERC20ResToBigInt(res, erc20TotalSupplyMethod)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unpackERC20ResToBigInt(res *evmtypes.MsgEthereumTxResponse, methodName string) (*big.Int, error) {
 | 
			
		||||
	if res.Failed() {
 | 
			
		||||
		if res.VmError == vm.ErrExecutionReverted.Error() {
 | 
			
		||||
			// Unpacks revert
 | 
			
		||||
@ -206,11 +236,11 @@ func (k Keeper) QueryERC20BalanceOf(
 | 
			
		||||
		return nil, status.Error(codes.Internal, res.VmError)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	anyOutput, err := types.ERC20MintableBurnableContract.ABI.Unpack(erc20BalanceOfMethod, res.Ret)
 | 
			
		||||
	anyOutput, err := types.ERC20MintableBurnableContract.ABI.Unpack(methodName, res.Ret)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
			"failed to unpack method %v response: %w",
 | 
			
		||||
			erc20BalanceOfMethod,
 | 
			
		||||
			methodName,
 | 
			
		||||
			err,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
@ -218,7 +248,7 @@ func (k Keeper) QueryERC20BalanceOf(
 | 
			
		||||
	if len(anyOutput) != 1 {
 | 
			
		||||
		return nil, fmt.Errorf(
 | 
			
		||||
			"invalid ERC20 %v call return outputs %v, expected %v",
 | 
			
		||||
			erc20BalanceOfMethod,
 | 
			
		||||
			methodName,
 | 
			
		||||
			len(anyOutput),
 | 
			
		||||
			1,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,27 @@ func (suite *ERC20TestSuite) TestERC20Mint() {
 | 
			
		||||
	suite.Require().Equal(big.NewInt(1234), balance)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ERC20TestSuite) TestQueryERC20TotalSupply() {
 | 
			
		||||
	suite.Run("with no balance", func() {
 | 
			
		||||
		bal, err := suite.Keeper.QueryERC20TotalSupply(suite.Ctx, suite.contractAddr)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		suite.BigIntsEqual(big.NewInt(0), bal, "expected total supply of 0")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("with balance", func() {
 | 
			
		||||
		amount := big.NewInt(1e10)
 | 
			
		||||
		expectedTotal := big.NewInt(3e10)
 | 
			
		||||
		// mint 1e10 to three random accounts
 | 
			
		||||
		suite.NoError(suite.Keeper.MintERC20(suite.Ctx, suite.contractAddr, testutil.RandomInternalEVMAddress(), amount))
 | 
			
		||||
		suite.NoError(suite.Keeper.MintERC20(suite.Ctx, suite.contractAddr, testutil.RandomInternalEVMAddress(), amount))
 | 
			
		||||
		suite.NoError(suite.Keeper.MintERC20(suite.Ctx, suite.contractAddr, testutil.RandomInternalEVMAddress(), amount))
 | 
			
		||||
 | 
			
		||||
		bal, err := suite.Keeper.QueryERC20TotalSupply(suite.Ctx, suite.contractAddr)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		suite.BigIntsEqual(expectedTotal, bal, "unexpected total supply after minting")
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *ERC20TestSuite) TestDeployKavaWrappedCosmosCoinERC20Contract() {
 | 
			
		||||
	suite.Run("fails to deploy invalid contract", func() {
 | 
			
		||||
		// empty other fields means this token is invalid.
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
package keeper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 | 
			
		||||
 | 
			
		||||
@ -11,6 +13,7 @@ import (
 | 
			
		||||
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))
 | 
			
		||||
}
 | 
			
		||||
@ -24,6 +27,9 @@ func AllInvariants(bankK types.BankKeeper, k Keeper) sdk.Invariant {
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -105,3 +111,38 @@ func BackedCoinsInvariant(_ types.BankKeeper, k Keeper) sdk.Invariant {
 | 
			
		||||
		return message, broken
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CosmosCoinsFullyBackedInvariant ensures the total supply of ERC20 representations of sdk.Coins
 | 
			
		||||
// match the balances in the module account.
 | 
			
		||||
//
 | 
			
		||||
// This invariant depends on the fact that coins can only become part of the balance through
 | 
			
		||||
// conversion to ERC20s.
 | 
			
		||||
// If in the future sdk.Coins can be sent directly to the module account,
 | 
			
		||||
// or the module account balance can be increased in any other way,
 | 
			
		||||
// this invariant should be changed from checking that the balance equals the total supply,
 | 
			
		||||
// to check that the balance is greater than or equal to the total supply.
 | 
			
		||||
func CosmosCoinsFullyBackedInvariant(bankK types.BankKeeper, k Keeper) sdk.Invariant {
 | 
			
		||||
	broken := false
 | 
			
		||||
	message := sdk.FormatInvariant(
 | 
			
		||||
		types.ModuleName,
 | 
			
		||||
		"cosmos coins fully-backed broken",
 | 
			
		||||
		"ERC20 total supply is not equal to module account balance",
 | 
			
		||||
	)
 | 
			
		||||
	maccAddress := authtypes.NewModuleAddress(types.ModuleName)
 | 
			
		||||
 | 
			
		||||
	return func(ctx sdk.Context) (string, bool) {
 | 
			
		||||
		k.IterateAllDeployedCosmosCoinContracts(ctx, func(c types.DeployedCosmosCoinContract) bool {
 | 
			
		||||
			moduleBalance := bankK.GetBalance(ctx, maccAddress, c.CosmosDenom).Amount
 | 
			
		||||
			totalSupply, err := k.QueryERC20TotalSupply(ctx, *c.Address)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic(fmt.Sprintf("failed to query total supply for %+v", c))
 | 
			
		||||
			}
 | 
			
		||||
			// expect total supply to equal balance in the module
 | 
			
		||||
			if totalSupply.Cmp(moduleBalance.BigInt()) != 0 {
 | 
			
		||||
				broken = true
 | 
			
		||||
			}
 | 
			
		||||
			return broken
 | 
			
		||||
		})
 | 
			
		||||
		return message, broken
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,12 @@ import (
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 | 
			
		||||
	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
 | 
			
		||||
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	"github.com/stretchr/testify/suite"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/x/evmutil/keeper"
 | 
			
		||||
	"github.com/kava-labs/kava/x/evmutil/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/x/evmutil/types"
 | 
			
		||||
@ -17,6 +21,9 @@ type invariantTestSuite struct {
 | 
			
		||||
	testutil.Suite
 | 
			
		||||
	invariants   map[string]map[string]sdk.Invariant
 | 
			
		||||
	contractAddr types.InternalEVMAddress
 | 
			
		||||
 | 
			
		||||
	cosmosCoin             types.AllowedCosmosCoinERC20Token
 | 
			
		||||
	cosmosCoinContractAddr types.InternalEVMAddress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInvariantTestSuite(t *testing.T) {
 | 
			
		||||
@ -62,6 +69,31 @@ func (suite *invariantTestSuite) SetupValidState() {
 | 
			
		||||
		big.NewInt(1000),
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	// setup a cosmos coin erc20 with supply
 | 
			
		||||
	tokenInfo := types.NewAllowedCosmosCoinERC20Token("magic", "Magic coin", "MAGIC", 6)
 | 
			
		||||
	suite.cosmosCoin = tokenInfo
 | 
			
		||||
	params := suite.Keeper.GetParams(suite.Ctx)
 | 
			
		||||
	params.AllowedCosmosDenoms = append(params.AllowedCosmosDenoms, tokenInfo)
 | 
			
		||||
	suite.Keeper.SetParams(suite.Ctx, params)
 | 
			
		||||
 | 
			
		||||
	suite.cosmosCoinContractAddr, err = suite.Keeper.GetOrDeployCosmosCoinERC20Contract(suite.Ctx, tokenInfo)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// setup converted coin position
 | 
			
		||||
	err = suite.Keeper.MintERC20(
 | 
			
		||||
		suite.Ctx,
 | 
			
		||||
		suite.cosmosCoinContractAddr,
 | 
			
		||||
		testutil.RandomInternalEVMAddress(),
 | 
			
		||||
		big.NewInt(1e12),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	err = suite.App.FundModuleAccount(
 | 
			
		||||
		suite.Ctx,
 | 
			
		||||
		types.ModuleName,
 | 
			
		||||
		sdk.NewCoins(sdk.NewInt64Coin(tokenInfo.CosmosDenom, 1e12)),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterRoutes implements sdk.InvariantRegistry
 | 
			
		||||
@ -137,3 +169,49 @@ func (suite *invariantTestSuite) TestSmallBalances() {
 | 
			
		||||
	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.
 | 
			
		||||
func (suite *invariantTestSuite) TestSendToModuleAccountNotAllowed() {
 | 
			
		||||
	bKeeper := suite.App.GetBankKeeper()
 | 
			
		||||
	maccAddress := authtypes.NewModuleAddress(types.ModuleName)
 | 
			
		||||
	suite.True(bKeeper.BlockedAddr(maccAddress))
 | 
			
		||||
 | 
			
		||||
	coins := sdk.NewCoins(sdk.NewInt64Coin(suite.cosmosCoin.CosmosDenom, 1e7))
 | 
			
		||||
	addr := app.RandomAddress()
 | 
			
		||||
 | 
			
		||||
	err := suite.App.FundAccount(suite.Ctx, addr, coins)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	bankMsgServer := bankkeeper.NewMsgServerImpl(bKeeper)
 | 
			
		||||
	_, err = bankMsgServer.Send(suite.Ctx, &banktypes.MsgSend{
 | 
			
		||||
		FromAddress: addr.String(),
 | 
			
		||||
		ToAddress:   maccAddress.String(),
 | 
			
		||||
		Amount:      coins,
 | 
			
		||||
	})
 | 
			
		||||
	suite.ErrorContains(err, "kava1w9vxuke5dz6hyza2j932qgmxltnfxwl78u920k is not allowed to receive funds: unauthorized")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *invariantTestSuite) TestCosmosCoinsFullyBackedInvariant() {
 | 
			
		||||
	invariantName := "cosmos-coins-fully-backed"
 | 
			
		||||
	// default state is valid
 | 
			
		||||
	_, broken := suite.runInvariant(invariantName, keeper.CosmosCoinsFullyBackedInvariant)
 | 
			
		||||
	suite.False(broken)
 | 
			
		||||
 | 
			
		||||
	suite.SetupValidState()
 | 
			
		||||
	_, broken = suite.runInvariant(invariantName, keeper.CosmosCoinsFullyBackedInvariant)
 | 
			
		||||
	suite.False(broken)
 | 
			
		||||
 | 
			
		||||
	// break the invariant by removing module account balance without adjusting token supply
 | 
			
		||||
	err := suite.BankKeeper.SendCoinsFromModuleToAccount(
 | 
			
		||||
		suite.Ctx,
 | 
			
		||||
		types.ModuleName,
 | 
			
		||||
		app.RandomAddress(),
 | 
			
		||||
		sdk.NewCoins(sdk.NewInt64Coin(suite.cosmosCoin.CosmosDenom, 1e5)),
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	_, broken = suite.runInvariant(invariantName, keeper.CosmosCoinsFullyBackedInvariant)
 | 
			
		||||
	suite.True(broken)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -212,3 +212,21 @@ func (k *Keeper) GetDeployedCosmosCoinContract(ctx sdk.Context, cosmosDenom stri
 | 
			
		||||
	found := len(bz) != 0
 | 
			
		||||
	return types.BytesToInternalEVMAddress(bz), found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IterateAllDeployedCosmosCoinContracts iterates through all the deployed ERC20 contracts representing
 | 
			
		||||
// cosmos-sdk coins. If true is returned from the callback, iteration is halted.
 | 
			
		||||
func (k Keeper) IterateAllDeployedCosmosCoinContracts(ctx sdk.Context, cb func(types.DeployedCosmosCoinContract) bool) {
 | 
			
		||||
	store := ctx.KVStore(k.storeKey)
 | 
			
		||||
	iterator := sdk.KVStorePrefixIterator(store, types.DeployedCosmosCoinContractKeyPrefix)
 | 
			
		||||
 | 
			
		||||
	defer iterator.Close()
 | 
			
		||||
	for ; iterator.Valid(); iterator.Next() {
 | 
			
		||||
		contract := types.NewDeployedCosmosCoinContract(
 | 
			
		||||
			types.DenomFromDeployedCosmosCoinContractKey(iterator.Key()),
 | 
			
		||||
			types.BytesToInternalEVMAddress(iterator.Value()),
 | 
			
		||||
		)
 | 
			
		||||
		if cb(contract) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -391,6 +391,49 @@ func (suite *keeperTestSuite) TestDeployedCosmosCoinContractStoreState() {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *keeperTestSuite) TestIterateAllDeployedCosmosCoinContracts() {
 | 
			
		||||
	suite.SetupTest()
 | 
			
		||||
	address := testutil.RandomInternalEVMAddress()
 | 
			
		||||
	denoms := []string{}
 | 
			
		||||
	register := func(denom string) {
 | 
			
		||||
		addr := testutil.RandomInternalEVMAddress()
 | 
			
		||||
		if denom == "waldo" {
 | 
			
		||||
			addr = address
 | 
			
		||||
		}
 | 
			
		||||
		err := suite.Keeper.SetDeployedCosmosCoinContract(suite.Ctx, denom, addr)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		denoms = append(denoms, denom)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// register some contracts
 | 
			
		||||
	register("magic")
 | 
			
		||||
	register("popcorn")
 | 
			
		||||
	register("waldo")
 | 
			
		||||
	register("zzz")
 | 
			
		||||
	register("ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2")
 | 
			
		||||
 | 
			
		||||
	suite.Run("stops when told", func() {
 | 
			
		||||
		// test out stopping the iteration
 | 
			
		||||
		// NOTE: don't actually look for a single contract this way. the keys are deterministic by denom.
 | 
			
		||||
		var contract types.DeployedCosmosCoinContract
 | 
			
		||||
		suite.Keeper.IterateAllDeployedCosmosCoinContracts(suite.Ctx, func(c types.DeployedCosmosCoinContract) bool {
 | 
			
		||||
			contract = c
 | 
			
		||||
			return c.CosmosDenom == "waldo"
 | 
			
		||||
		})
 | 
			
		||||
		suite.Equal(types.NewDeployedCosmosCoinContract("waldo", address), contract)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("iterates all contracts", func() {
 | 
			
		||||
		foundDenoms := make([]string, 0, len(denoms))
 | 
			
		||||
		suite.Keeper.IterateAllDeployedCosmosCoinContracts(suite.Ctx, func(c types.DeployedCosmosCoinContract) bool {
 | 
			
		||||
			foundDenoms = append(foundDenoms, c.CosmosDenom)
 | 
			
		||||
			return false
 | 
			
		||||
		})
 | 
			
		||||
		suite.Len(foundDenoms, len(denoms))
 | 
			
		||||
		suite.ElementsMatch(denoms, foundDenoms)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestKeeperTestSuite(t *testing.T) {
 | 
			
		||||
	suite.Run(t, new(keeperTestSuite))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user