Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(erc20): add indexes at genesis state to track active token pairs #68

Merged
merged 10 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
1,222 changes: 1,162 additions & 60 deletions api/canto/erc20/v1/erc20.pulsar.go

Large diffs are not rendered by default.

376 changes: 344 additions & 32 deletions api/canto/erc20/v1/genesis.pulsar.go

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions proto/canto/erc20/v1/erc20.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,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.
// Deprecated: This legacy proposal is deprecated in favor of Msg-based gov
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 @@ -13,6 +13,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
23 changes: 18 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,30 @@ 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 contracts at the same denom can exist,
// but only one which is in indexes are 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),
}
}
111 changes: 89 additions & 22 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,66 @@ import (
"github.com/Canto-Network/Canto/v7/x/erc20/types"
)

var (
uqstars = "ibc/13B6057538B93225F6EBACCB64574C49B2C1568C5AE6CCFE0A039D7DAC02BF29"
// uqstars1 and uqstars2 have same denom
// uqstars1 is deployed first.
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 = []types.TokenPairDenomIndex{
{
Denom: customERC20.Denom,
TokenPairId: customERC20.GetID(),
},
{
Denom: uqstars,
// denomIdx must have latest token pair id
zsystm marked this conversation as resolved.
Show resolved Hide resolved
// if there are multiple token pairs with the same denom
TokenPairId: uqstars2.GetID(),
},
}
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 Down Expand Up @@ -82,30 +143,33 @@ 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,
),
},
}

for _, tc := range testCases {

suite.Require().NotPanics(func() {
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)
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)
suite.Len(suite.app.Erc20Keeper.GetAllTokenPairERC20AddressIndexes(suite.ctx), 0)
}
}
}
Expand All @@ -127,14 +191,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 +207,18 @@ func (suite *GenesisTestSuite) TestErc20ExportGenesis() {

tokenPairs := suite.app.Erc20Keeper.GetTokenPairs(suite.ctx)
if len(tokenPairs) > 0 {
suite.Require().Equal(genesisExported.TokenPairs, tokenPairs)
suite.Require().Equal(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 @@ -90,7 +90,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 @@ -136,8 +136,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 @@ -114,8 +114,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 @@ -129,8 +129,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
8 changes: 4 additions & 4 deletions x/erc20/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1594,8 +1594,8 @@ func (suite *KeeperTestSuite) TestMsgExecutionByProposal() {
id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, ibcBase)
pair, ok := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
suite.Require().True(ok)
suite.Require().Equal(suite.app.Erc20Keeper.GetDenomMap(suite.ctx, pair.Denom), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetERC20Map(suite.ctx, common.HexToAddress(pair.Erc20Address)), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetTokenPairIdByDenom(suite.ctx, pair.Denom), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetTokenPairIdByERC20Addr(suite.ctx, common.HexToAddress(pair.Erc20Address)), id)
},
false,
},
Expand Down Expand Up @@ -1628,8 +1628,8 @@ func (suite *KeeperTestSuite) TestMsgExecutionByProposal() {
id := suite.app.Erc20Keeper.GetTokenPairID(suite.ctx, erc20Address)
pair, ok := suite.app.Erc20Keeper.GetTokenPair(suite.ctx, id)
suite.Require().True(ok)
suite.Require().Equal(suite.app.Erc20Keeper.GetDenomMap(suite.ctx, pair.Denom), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetERC20Map(suite.ctx, common.HexToAddress(pair.Erc20Address)), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetTokenPairIdByDenom(suite.ctx, pair.Denom), id)
suite.Require().Equal(suite.app.Erc20Keeper.GetTokenPairIdByERC20Addr(suite.ctx, common.HexToAddress(pair.Erc20Address)), id)
},
false,
},
Expand Down
8 changes: 4 additions & 4 deletions x/erc20/keeper/proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func (k Keeper) RegisterCoin(

pair := types.NewTokenPair(addr, coinMetadata.Base, true, types.OWNER_MODULE)
k.SetTokenPair(ctx, pair)
k.SetDenomMap(ctx, pair.Denom, pair.GetID())
k.SetERC20Map(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
k.SetTokenPairIdByDenom(ctx, pair.Denom, pair.GetID())
k.SetTokenPairIdByERC20Addr(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())

return &pair, nil
}
Expand Down Expand Up @@ -98,8 +98,8 @@ func (k Keeper) RegisterERC20(

pair := types.NewTokenPair(contract, metadata.Name, true, types.OWNER_EXTERNAL)
k.SetTokenPair(ctx, pair)
k.SetDenomMap(ctx, pair.Denom, pair.GetID())
k.SetERC20Map(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
k.SetTokenPairIdByDenom(ctx, pair.Denom, pair.GetID())
k.SetTokenPairIdByERC20Addr(ctx, common.HexToAddress(pair.Erc20Address), pair.GetID())
return &pair, nil
}

Expand Down
Loading
Loading