Skip to content

Commit

Permalink
add class id & tokeninfo querier
Browse files Browse the repository at this point in the history
  • Loading branch information
sh-cha committed Jan 15, 2025
1 parent f82cee0 commit 2ce796b
Show file tree
Hide file tree
Showing 10 changed files with 1,655 additions and 109 deletions.
35 changes: 35 additions & 0 deletions proto/minievm/evm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ service Query {
option (google.api.http).get = "/minievm/evm/v1/contracts/by_denom";
}

// ERC721ClassIdByContractAddr gets the class id by contract address.
rpc ERC721ClassIdByContractAddr(QueryERC721ClassIdByContractAddrRequest) returns (QueryERC721ClassIdByContractAddrResponse) {
option (google.api.http).get = "/minievm/evm/v1/erc721/class_id/{contract_addr}";
}

// ERC721OriginTokenInfos gets the origin token infos by class id and token ids.
rpc ERC721OriginTokenInfos(QueryERC721OriginTokenInfosRequest) returns (QueryERC721OriginTokenInfosResponse) {
option (google.api.http).get = "/minievm/evm/v1/erc721/origin_token_infos/{class_id}/{token_ids}";
}

// Denom gets the denom of the given contract address.
rpc Denom(QueryDenomRequest) returns (QueryDenomResponse) {
option (google.api.http).get = "/minievm/evm/v1/denoms/{contract_addr}";
Expand Down Expand Up @@ -185,6 +195,31 @@ message QueryCallResponse {
string error = 5;
}

// QueryERC721ClassIdsByContractAddrRequest is the request type for the Query/ERC721ClassIdsByContractAddr RPC
// method
message QueryERC721ClassIdByContractAddrRequest {
string contract_addr = 1;
}

// QueryERC721ClassIdsByContractAddrResponse is the response type for the Query/ERC721ClassIdsByContractAddr RPC
// method
message QueryERC721ClassIdByContractAddrResponse {
string class_id = 1;
}

// QueryERC721OriginTokenInfosRequest is the request type for the Query/ERC721OriginTokenInfos RPC
// method
message QueryERC721OriginTokenInfosRequest {
string class_id = 1;
repeated string token_ids = 2;
}

// QueryERC721OriginTokenInfosResponse is the response type for the Query/ERC721OriginTokenInfos RPC
// method
message QueryERC721OriginTokenInfosResponse {
repeated ERC721OriginTokenInfo token_infos = 1;
}

// QueryParamsRequest is the request type for the Query/Params RPC method.
message QueryParamsRequest {}

Expand Down
5 changes: 5 additions & 0 deletions proto/minievm/evm/v1/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,8 @@ message AccessTuple {
// These keys represent specific storage slots in the contract's storage that are accessed or modified.
repeated string storage_keys = 2;
}

message ERC721OriginTokenInfo {
string token_origin_id = 1;
string token_uri = 2;
}
48 changes: 48 additions & 0 deletions x/evm/keeper/erc721.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ func (k ERC721Keeper) GetTokenInfos(ctx context.Context, classId string, tokenId
return tokenUris, make([]string, len(tokenIds)), err
}

func (k ERC721Keeper) GetOriginTokenInfos(ctx context.Context, classId string, tokenIds []*big.Int) (tokenOriginIds, tokenUris []string, err error) {
contractAddr, err := types.ContractAddressFromClassId(ctx, k, classId)
if err != nil {
return nil, nil, err
}

Check warning on line 289 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L288-L289

Added lines #L288 - L289 were not covered by tests

tokenOriginIds = make([]string, len(tokenIds))
tokenUris = make([]string, len(tokenIds))
for i, tokenId := range tokenIds {
tokenUri, err := k.tokenURI(ctx, tokenId, contractAddr)
if err != nil {
return nil, nil, err
}

Check warning on line 297 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L296-L297

Added lines #L296 - L297 were not covered by tests
tokenUris[i] = tokenUri

tokenOriginId, err := k.tokenOriginId(ctx, tokenId, contractAddr)
if err != nil {
return nil, nil, err
}

Check warning on line 303 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L302-L303

Added lines #L302 - L303 were not covered by tests
tokenOriginIds[i] = tokenOriginId
}
return tokenOriginIds, tokenUris, err
}

func (k ERC721Keeper) balanceOf(ctx context.Context, addr, contractAddr common.Address) (math.Int, error) {
inputBz, err := k.ABI.Pack("balanceOf", addr)
if err != nil {
Expand Down Expand Up @@ -405,3 +429,27 @@ func (k ERC721Keeper) tokenURI(ctx context.Context, tokenId *big.Int, contractAd

return tokenUri, nil
}

func (k ERC721Keeper) tokenOriginId(ctx context.Context, tokenId *big.Int, contractAddr common.Address) (string, error) {
inputBz, err := k.ABI.Pack("tokenOriginId", tokenId)
if err != nil {
return "", types.ErrFailedToPackABI.Wrap(err.Error())
}

Check warning on line 437 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L436-L437

Added lines #L436 - L437 were not covered by tests

retBz, err := k.EVMStaticCall(ctx, types.NullAddress, contractAddr, inputBz, nil)
if err != nil {
return "", err
}

Check warning on line 442 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L441-L442

Added lines #L441 - L442 were not covered by tests

res, err := k.ABI.Unpack("tokenOriginId", retBz)
if err != nil {
return "", types.ErrFailedToUnpackABI.Wrap(err.Error())
}

Check warning on line 447 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L446-L447

Added lines #L446 - L447 were not covered by tests

tokenOriginId, ok := res[0].(string)
if !ok {
return tokenOriginId, types.ErrFailedToDecodeOutput
}

Check warning on line 452 in x/evm/keeper/erc721.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc721.go#L451-L452

Added lines #L451 - L452 were not covered by tests

return tokenOriginId, nil
}
58 changes: 58 additions & 0 deletions x/evm/keeper/erc721_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"math/big"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -162,3 +163,60 @@ func Test_TransferNFTs(t *testing.T) {
require.Equal(t, receiverAddr, owner)
}
}

func Test_GetContractAddrByClassId(t *testing.T) {
ctx, input := createDefaultTestInput(t)
evmKeeper := input.EVMKeeper
ierc721Keeper, err := keeper.NewERC721Keeper(&evmKeeper)
require.NoError(t, err)
erc721Keeper := ierc721Keeper.(*keeper.ERC721Keeper)

classId := "ibc/test-class-id"
classUri := "test-class-uri"
classData := "test-class-data"

contractAddr := crypto.CreateAddress(types.StdAddress, 2)

err = erc721Keeper.CreateOrUpdateClass(ctx, classId, classUri, classData)
require.NoError(t, err)

actualClassId, err := evmKeeper.GetClassIdByContractAddr(ctx, contractAddr)
require.NoError(t, err)
require.Equal(t, classId, actualClassId)
}

func Test_GetOriginTokenInfos(t *testing.T) {
ctx, input := createDefaultTestInput(t)
evmKeeper := input.EVMKeeper
ierc721Keeper, err := keeper.NewERC721Keeper(&evmKeeper)
require.NoError(t, err)
erc721Keeper := ierc721Keeper.(*keeper.ERC721Keeper)

classId := "ibc/test-class-id"
classUri := "test-class-uri"
classData := "test-class-data"

err = erc721Keeper.CreateOrUpdateClass(ctx, classId, classUri, classData)
require.NoError(t, err)

_, _, addr := keyPubAddr()

tokenIds := []string{"test-token-id", "token-idasdfasdf", "2198372123"}
tokenUris := []string{"test-token-uri", "", "23123"}
tokenDatas := []string{"test-token-data", "", "1239827194812"}

err = erc721Keeper.Mints(ctx, addr, classId, tokenIds, tokenUris, tokenDatas)
require.NoError(t, err)

evmTokenIds := make([]*big.Int, 0)
for i := range tokenIds {
tokenId, ok := types.TokenIdToBigInt(classId, tokenIds[i])
require.True(t, ok)
evmTokenIds = append(evmTokenIds, tokenId)
}

tokenOriginIds, actualTokenUris, err := erc721Keeper.GetOriginTokenInfos(ctx, classId, evmTokenIds)
require.NoError(t, err)
require.Equal(t, tokenIds, tokenOriginIds)
require.Equal(t, tokenUris, actualTokenUris)
}
22 changes: 22 additions & 0 deletions x/evm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"math/big"
"sync/atomic"

"cosmossdk.io/collections"
Expand Down Expand Up @@ -218,6 +219,27 @@ func (k Keeper) GetClassIdByContractAddr(ctx context.Context, contractAddr commo
return k.ERC721ClassIdsByContractAddr.Get(ctx, contractAddr.Bytes())
}

// GetClassUriByContractAddr returns class uri by contract address
func (k Keeper) GetClassUriByContractAddr(ctx context.Context, contractAddr common.Address) (string, error) {
return k.ERC721ClassURIs.Get(ctx, contractAddr.Bytes())

Check warning on line 224 in x/evm/keeper/keeper.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/keeper.go#L223-L224

Added lines #L223 - L224 were not covered by tests
}

// GetOriginTokenInfos returns token origin ids and token uris
func (k Keeper) GetOriginTokenInfos(ctx context.Context, classId string, tokenIdStrings []string) (tokenOriginIds, tokenUris []string, err error) {
tokenIds := make([]*big.Int, 0)
for _, tokenId := range tokenIdStrings {
tokenId, ok := new(big.Int).SetString(tokenId, 10)
if !ok {
return nil, nil, types.ErrInvalidTokenId
}

Check warning on line 234 in x/evm/keeper/keeper.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/keeper.go#L233-L234

Added lines #L233 - L234 were not covered by tests
tokenIds = append(tokenIds, tokenId)
}

erc721Keeper := k.ERC721Keeper().(*ERC721Keeper)
tokenOriginIds, tokenUris, err = erc721Keeper.GetOriginTokenInfos(ctx, classId, tokenIds)
return
}

func (k Keeper) GetERC20FactoryAddr(ctx context.Context) (common.Address, error) {
factoryAddr, err := k.ERC20FactoryAddr.Get(ctx)
if err != nil {
Expand Down
33 changes: 33 additions & 0 deletions x/evm/keeper/query_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,39 @@ func (qs *queryServerImpl) Denom(ctx context.Context, req *types.QueryDenomReque
return &types.QueryDenomResponse{Denom: denom}, nil
}

// ERC721ClassIdByContractAddr implements types.QueryServer.
func (qs *queryServerImpl) ERC721ClassIdByContractAddr(ctx context.Context, req *types.QueryERC721ClassIdByContractAddrRequest) (*types.QueryERC721ClassIdByContractAddrResponse, error) {
contractAddr, err := types.ContractAddressFromString(qs.ac, req.ContractAddr)
if err != nil {
return nil, err
}

Check warning on line 203 in x/evm/keeper/query_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/query_server.go#L202-L203

Added lines #L202 - L203 were not covered by tests

classId, err := qs.Keeper.GetClassIdByContractAddr(ctx, contractAddr)
if err != nil {
return nil, err
}

Check warning on line 208 in x/evm/keeper/query_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/query_server.go#L207-L208

Added lines #L207 - L208 were not covered by tests

return &types.QueryERC721ClassIdByContractAddrResponse{ClassId: classId}, nil
}

// ERC721OriginTokenInfos implements types.QueryServer.
func (qs *queryServerImpl) ERC721OriginTokenInfos(ctx context.Context, req *types.QueryERC721OriginTokenInfosRequest) (*types.QueryERC721OriginTokenInfosResponse, error) {
tokenOriginIds, tokenUris, err := qs.Keeper.GetOriginTokenInfos(ctx, req.ClassId, req.TokenIds)
if err != nil {
return nil, err
}

Check warning on line 218 in x/evm/keeper/query_server.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/query_server.go#L217-L218

Added lines #L217 - L218 were not covered by tests

tokenInfos := make([]*types.ERC721OriginTokenInfo, len(tokenOriginIds))
for i, tokenOriginId := range tokenOriginIds {
tokenInfos[i] = &types.ERC721OriginTokenInfo{
TokenOriginId: tokenOriginId,
TokenUri: tokenUris[i],
}
}

return &types.QueryERC721OriginTokenInfosResponse{TokenInfos: tokenInfos}, nil
}

// Params implements types.QueryServer.
func (qs *queryServerImpl) Params(ctx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
params, err := qs.Keeper.Params.Get(ctx)
Expand Down
72 changes: 72 additions & 0 deletions x/evm/keeper/query_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"

"github.com/initia-labs/minievm/x/evm/contracts/erc20"
"github.com/initia-labs/minievm/x/evm/keeper"
Expand Down Expand Up @@ -78,3 +79,74 @@ func Test_Query_ERC20Wrapper(t *testing.T) {
require.Equal(t, wrapperAddr.Hex(), res.Address)
}

func Test_Query_ERC721ClassIdByContractAddr(t *testing.T) {
ctx, input := createDefaultTestInput(t)

evmKeeper := input.EVMKeeper
erc721Keeper, err := keeper.NewERC721Keeper(&evmKeeper)
require.NoError(t, err)

classId := "test-class-id"
classUri := "test-class-uri"

contractAddr := crypto.CreateAddress(types.StdAddress, 2)

err = erc721Keeper.CreateOrUpdateClass(ctx, classId, classUri, "")
require.NoError(t, err)

qs := keeper.NewQueryServer(&input.EVMKeeper)
res, err := qs.ERC721ClassIdByContractAddr(ctx, &types.QueryERC721ClassIdByContractAddrRequest{
ContractAddr: contractAddr.Hex(),
})
require.NoError(t, err)

require.Equal(t, classId, res.ClassId)
}

func Test_Query_ERC721OriginTokenInfos(t *testing.T) {
ctx, input := createDefaultTestInput(t)

evmKeeper := input.EVMKeeper
ierc721Keeper, err := keeper.NewERC721Keeper(&evmKeeper)
require.NoError(t, err)
erc721Keeper := ierc721Keeper.(*keeper.ERC721Keeper)

classId := "ibc/test-class-id"
classUri := "test-class-uri"
classData := "test-class-data"

err = erc721Keeper.CreateOrUpdateClass(ctx, classId, classUri, classData)
require.NoError(t, err)

_, _, addr := keyPubAddr()

tokenIds := []string{"test-token-id", "token-idasdfasdf", "2198372123"}
tokenUris := []string{"test-token-uri", "", "23123"}
tokenDatas := []string{"test-token-data", "", "1239827194812"}

err = erc721Keeper.Mints(ctx, addr, classId, tokenIds, tokenUris, tokenDatas)
require.NoError(t, err)

evmTokenIds := make([]string, 0)
for i := range tokenIds {
tokenId, ok := types.TokenIdToBigInt(classId, tokenIds[i])
require.True(t, ok)
evmTokenIds = append(evmTokenIds, tokenId.String())
}

qs := keeper.NewQueryServer(&input.EVMKeeper)
res, err := qs.ERC721OriginTokenInfos(ctx, &types.QueryERC721OriginTokenInfosRequest{
ClassId: classId,
TokenIds: evmTokenIds,
})
require.NoError(t, err)

actualTokenIds := make([]string, 0)
actualTokenUris := make([]string, 0)
for i := range res.TokenInfos {
actualTokenIds = append(actualTokenIds, res.TokenInfos[i].TokenOriginId)
actualTokenUris = append(actualTokenUris, res.TokenInfos[i].TokenUri)
}
require.Equal(t, tokenIds, actualTokenIds)
require.Equal(t, tokenUris, actualTokenUris)
}
Loading

0 comments on commit 2ce796b

Please sign in to comment.