Skip to content
This repository has been archived by the owner on Jul 30, 2024. It is now read-only.

Fix: wrong denom check at RegsterCoin and remove IBC-related tests #6

Merged
merged 7 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions proto/canto/erc20/v1/erc20.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ message TokenPair {
Owner contract_owner = 4;
}

// TokenPairDenomIndex is a mapping of a token pair's denom to its token pair
// ID.
message TokenPairDenomIndex {
option (gogoproto.equal) = true;
string denom = 1;
bytes token_pair_id = 2;
}

// TokenPairERC20AddressIndex is a mapping of a token pair's ERC20 address to
// its token pair ID.
message TokenPairERC20AddressIndex {
option (gogoproto.equal) = true;
bytes erc20_address = 1;
bytes token_pair_id = 2;
}

// RegisterCoinProposal is a gov Content type to register a token pair for a
// native Cosmos coin.
message RegisterCoinProposal {
Expand Down
8 changes: 8 additions & 0 deletions proto/canto/erc20/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ message GenesisState {
Params params = 1 [ (gogoproto.nullable) = false ];
// registered token pairs
repeated TokenPair token_pairs = 2 [ (gogoproto.nullable) = false ];
// list of mappings from Cosmos denoms to token pair IDs, used for indexing
// token pairs by their denom
repeated TokenPairDenomIndex denom_indexes = 3
[ (gogoproto.nullable) = false ];
// list of mappings from ERC20 addresses to token pair IDs, used for indexing
// token pairs by their ERC20 address
repeated TokenPairERC20AddressIndex erc20_address_indexes = 4
[ (gogoproto.nullable) = false ];
}

// Params defines the erc20 module params
Expand Down
24 changes: 19 additions & 5 deletions x/erc20/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package erc20
import (
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/ethereum/go-ethereum/common"

"github.com/Canto-Network/Canto/v7/x/erc20/keeper"
"github.com/Canto-Network/Canto/v7/x/erc20/types"
Expand All @@ -23,18 +24,31 @@ func InitGenesis(
panic("the erc20 module account has not been set")
}

// set token pair once
for _, pair := range data.TokenPairs {
id := pair.GetID()
k.SetTokenPair(ctx, pair)
k.SetDenomMap(ctx, pair.Denom, id)
k.SetERC20Map(ctx, pair.GetERC20Contract(), id)
}

// set indexes
// multiple token pairs should not be registered with the same denom,
// but if this happens, only the one in the index is valid.
for _, idx := range data.DenomIndexes {
id := idx.GetTokenPairId()
k.SetTokenPairIdByDenom(ctx, idx.Denom, id)
}
for _, idx := range data.Erc20AddressIndexes {
id := idx.GetTokenPairId()
k.SetTokenPairIdByERC20Addr(ctx, common.BytesToAddress(idx.Erc20Address), id)
}

}

// ExportGenesis export module status
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
return &types.GenesisState{
Params: k.GetParams(ctx),
TokenPairs: k.GetTokenPairs(ctx),
Params: k.GetParams(ctx),
TokenPairs: k.GetTokenPairs(ctx),
DenomIndexes: k.GetAllTokenPairDenomIndexes(ctx),
Erc20AddressIndexes: k.GetAllTokenPairERC20AddressIndexes(ctx),
}
}
117 changes: 96 additions & 21 deletions x/erc20/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/suite"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -21,6 +22,70 @@ import (
"github.com/Canto-Network/Canto/v7/x/erc20/types"
)

var (
uqstars = "utestdenom"
// The following test codes are to prove that multiple token pairs should not be registered
// with the same denom, but if this happens, only the one in the index is valid.
// uqstars1 and uqstars2 have same denom
// uqstars1 is deployed first.
zsystm marked this conversation as resolved.
Show resolved Hide resolved
uqstars1 = types.TokenPair{
Erc20Address: "0x2C68D1d6aB986Ff4640b51e1F14C716a076E44C4",
Denom: uqstars,
Enabled: true,
ContractOwner: types.OWNER_MODULE,
}
// uqstars2 is deployed later than uqstars1.
uqstars2 = types.TokenPair{
Erc20Address: "0xD32eB974468ed767338533842D2D4Cc90B9BAb46",
Denom: uqstars,
Enabled: true,
ContractOwner: types.OWNER_MODULE,
}
customERC20 = types.TokenPair{
Erc20Address: "0xC5e00D3b04563950941f7137B5AfA3a534F0D6d6",
Denom: "custom",
Enabled: true,
ContractOwner: types.OWNER_EXTERNAL,
}

tokenPairs = []types.TokenPair{
uqstars2,
// even if we put uqstars1 later, it should be disabled because
// uqstars2 is the deployed later than uqstars1
uqstars1,
customERC20,
}
// denomIdxs should be ordered by denom ascending to compare with GetAllTokenPairDenomIndexes
denomIdxs = []types.TokenPairDenomIndex{
{
Denom: customERC20.Denom,
TokenPairId: customERC20.GetID(),
},
{
Denom: uqstars,
// denomIdx must have the latest token pair id assigned
// if there are multiple token pairs with the same denom
TokenPairId: uqstars2.GetID(),
},
}
// erc20AddrIdxs should be ordered by erc20address ascending to compare with GetAllTokenPairERC20AddressIndexes
erc20AddrIdxs = []types.TokenPairERC20AddressIndex{
{
Erc20Address: common.HexToAddress(uqstars1.Erc20Address).Bytes(),
TokenPairId: uqstars2.GetID(),
},
{
Erc20Address: customERC20.GetERC20Contract().Bytes(),
TokenPairId: customERC20.GetID(),
},

{
Erc20Address: common.HexToAddress(uqstars2.Erc20Address).Bytes(),
TokenPairId: uqstars2.GetID(),
},
}
)

type GenesisTestSuite struct {
suite.Suite
ctx sdk.Context
Expand All @@ -39,7 +104,7 @@ func (suite *GenesisTestSuite) SetupTest() {
suite.app = app.Setup(false, feemarkettypes.DefaultGenesisState())
suite.ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{
Height: 1,
ChainID: "canto_9000-1",
ChainID: "test-chainid",
dongsam marked this conversation as resolved.
Show resolved Hide resolved
Time: time.Now().UTC(),
ProposerAddress: consAddress.Bytes(),

Expand Down Expand Up @@ -82,14 +147,10 @@ func (suite *GenesisTestSuite) TestERC20InitGenesis() {
"custom genesis",
types.NewGenesisState(
types.DefaultParams(),
[]types.TokenPair{
{
Erc20Address: "0x5dCA2483280D9727c80b5518faC4556617fb19ZZ",
Denom: "coin",
Enabled: true,
ContractOwner: types.OWNER_MODULE,
},
}),
tokenPairs,
denomIdxs,
erc20AddrIdxs,
),
},
}

Expand All @@ -99,13 +160,23 @@ func (suite *GenesisTestSuite) TestERC20InitGenesis() {
erc20.InitGenesis(suite.ctx, suite.app.Erc20Keeper, suite.app.AccountKeeper, tc.genesisState)
})
params := suite.app.Erc20Keeper.GetParams(suite.ctx)
suite.Require().Equal(tc.genesisState.Params, params)

tokenPairs := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
suite.Require().Equal(tc.genesisState.Params, params)
if len(tokenPairs) > 0 {
suite.Require().Equal(tc.genesisState.TokenPairs, tokenPairs)
// The `tokenPairs` may not be sorted by tokenpair.id. For testing purposes, only check if the elements match.
suite.Require().ElementsMatch(tc.genesisState.TokenPairs, tokenPairs)
suite.Equal(denomIdxs, suite.app.Erc20Keeper.GetAllTokenPairDenomIndexes(suite.ctx))
suite.Equal(erc20AddrIdxs, suite.app.Erc20Keeper.GetAllTokenPairERC20AddressIndexes(suite.ctx))
suite.Equal(
uqstars2.GetID(), suite.app.Erc20Keeper.GetTokenPairIdByDenom(suite.ctx, uqstars),
"denom index must have latest token pair id",
)
} else {
suite.Require().Len(tc.genesisState.TokenPairs, 0)
suite.Len(tc.genesisState.TokenPairs, 0)
suite.Len(suite.app.Erc20Keeper.GetAllTokenPairDenomIndexes(suite.ctx), 0)
}
}
}
Expand All @@ -127,14 +198,10 @@ func (suite *GenesisTestSuite) TestErc20ExportGenesis() {
"custom genesis",
types.NewGenesisState(
types.DefaultParams(),
[]types.TokenPair{
{
Erc20Address: "0x5dCA2483280D9727c80b5518faC4556617fb19ZZ",
Denom: "coin",
Enabled: true,
ContractOwner: types.OWNER_MODULE,
},
}),
tokenPairs,
denomIdxs,
erc20AddrIdxs,
),
},
}

Expand All @@ -147,11 +214,19 @@ func (suite *GenesisTestSuite) TestErc20ExportGenesis() {

tokenPairs := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
if len(tokenPairs) > 0 {
suite.Require().Equal(genesisExported.TokenPairs, tokenPairs)
// The `tokenPairs` may not be sorted by tokenpair.id. For testing purposes, only check if the elements match.
suite.Require().ElementsMatch(tc.genesisState.TokenPairs, tokenPairs)
suite.Equal(denomIdxs, suite.app.Erc20Keeper.GetAllTokenPairDenomIndexes(suite.ctx))
suite.Equal(erc20AddrIdxs, suite.app.Erc20Keeper.GetAllTokenPairERC20AddressIndexes(suite.ctx))
suite.Equal(
uqstars2.GetID(), suite.app.Erc20Keeper.GetTokenPairIdByDenom(suite.ctx, uqstars),
"denom index must have latest token pair id",
)
} else {
suite.Require().Len(genesisExported.TokenPairs, 0)
suite.Len(tc.genesisState.TokenPairs, 0)
suite.Len(suite.app.Erc20Keeper.GetAllTokenPairDenomIndexes(suite.ctx), 0)
suite.Len(suite.app.Erc20Keeper.GetAllTokenPairERC20AddressIndexes(suite.ctx), 0)
}
})
// }
}
}
2 changes: 1 addition & 1 deletion x/erc20/keeper/evm_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (h Hooks) PostTxProcessing(

// Check that the contract is a registered token pair
contractAddr := log.Address
id := h.k.GetERC20Map(ctx, contractAddr)
id := h.k.GetTokenPairIdByERC20Addr(ctx, contractAddr)
if len(id) == 0 {
continue
}
Expand Down
4 changes: 2 additions & 2 deletions x/erc20/keeper/evm_hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ func (suite *KeeperTestSuite) TestEvmHooksRegisteredERC20() {

suite.app.Erc20Keeper.DeleteTokenPair(suite.ctx, *pair)

suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, pair.GetERC20Contract(), pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, pair.Denom, pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, pair.GetERC20Contract(), pair.GetID())
// Mint 10 tokens to suite.address (owner)
_ = suite.MintERC20Token(contractAddr, suite.address, suite.address, big.NewInt(10))
suite.Commit()
Expand Down
8 changes: 4 additions & 4 deletions x/erc20/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ func (suite *KeeperTestSuite) TestTokenPair() {
addr := tests.GenerateAddress()
pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, pair)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, addr, pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, pair.Denom, pair.GetID())

req = &types.QueryTokenPairRequest{
Token: pair.Erc20Address,
Expand All @@ -131,8 +131,8 @@ func (suite *KeeperTestSuite) TestTokenPair() {
func() {
addr := tests.GenerateAddress()
pair := types.NewTokenPair(addr, "coin", true, types.OWNER_MODULE)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, addr, pair.GetID())
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, pair.Denom, pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, addr, pair.GetID())
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, pair.Denom, pair.GetID())

req = &types.QueryTokenPairRequest{
Token: pair.Erc20Address,
Expand Down
20 changes: 10 additions & 10 deletions x/erc20/keeper/mint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ func (suite *KeeperTestSuite) TestMintingEnabled() {
func() {
expPair.Enabled = false
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, expPair.GetERC20Contract(), id)
},
false,
},
Expand All @@ -50,8 +50,8 @@ func (suite *KeeperTestSuite) TestMintingEnabled() {
func() {
expPair.Enabled = true
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, expPair.GetERC20Contract(), id)

params := banktypes.DefaultParams()
params.SendEnabled = []*banktypes.SendEnabled{
Expand All @@ -64,17 +64,17 @@ func (suite *KeeperTestSuite) TestMintingEnabled() {
{
"token not registered",
func() {
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, expPair.GetERC20Contract(), id)
},
false,
},
{
"receiver address is blocked (module account)",
func() {
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, expPair.GetERC20Contract(), id)

acc := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.ModuleName)
receiver = acc.GetAddress()
Expand All @@ -85,8 +85,8 @@ func (suite *KeeperTestSuite) TestMintingEnabled() {
"ok",
func() {
suite.app.Erc20Keeper.SetTokenPair(suite.ctx, expPair)
suite.app.Erc20Keeper.SetDenomMap(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetERC20Map(suite.ctx, expPair.GetERC20Contract(), id)
suite.app.Erc20Keeper.SetTokenPairIdByDenom(suite.ctx, expPair.Denom, id)
suite.app.Erc20Keeper.SetTokenPairIdByERC20Addr(suite.ctx, expPair.GetERC20Contract(), id)

receiver = sdk.AccAddress(tests.GenerateAddress().Bytes())
},
Expand Down
Loading
Loading