Skip to content

Commit

Permalink
Merge branch 'main' into erc20/fun-token-tx
Browse files Browse the repository at this point in the history
  • Loading branch information
onikonychev committed Jun 29, 2024
2 parents 8683f97 + d920b11 commit 202a3bd
Show file tree
Hide file tree
Showing 7 changed files with 738 additions and 92 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1936](https://github.com/NibiruChain/nibiru/pull/1936) - feat(evm): EVM fungible token protobufs and encoding tests
- [#1946](https://github.com/NibiruChain/nibiru/pull/1946) - feat(evm): tx for creating fun token from coin
- [#1947](https://github.com/NibiruChain/nibiru/pull/1947) - fix(evm): fix FunToken state marshalling
- [#1949](https://github.com/NibiruChain/nibiru/pull/1949) - feat(evm): add fungible token mapping queries

#### Dapp modules: perp, spot, oracle, etc

Expand Down
30 changes: 30 additions & 0 deletions eth/rpc/backend/mocks/evm_query_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions proto/eth/evm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ service Query {
rpc BaseFee(QueryBaseFeeRequest) returns (QueryBaseFeeResponse) {
option (google.api.http).get = "/nibiru/evm/v1/base_fee";
}

rpc TokenMapping(QueryTokenMappingRequest) returns (QueryTokenMappingResponse) {
option (google.api.http).get = "/nibiru/evm/v1/token_mapping/{token}";
}
}

// QueryEthAccountRequest is the request type for the Query/Account RPC method.
Expand Down Expand Up @@ -301,3 +305,19 @@ message QueryBaseFeeResponse {
// base_fee is the EIP1559 base fee
string base_fee = 1 [(gogoproto.customtype) = "cosmossdk.io/math.Int"];
}

message QueryTokenMappingRequest {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// either the 0x contract address of the ERC-20 token or the cosmos denom
string token = 1;
}

message QueryTokenMappingResponse {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// fun_token is a mapping between the Cosmos native coin and the ERC20 contract address
FunToken fun_token = 1;
}
27 changes: 27 additions & 0 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,3 +740,30 @@ func (k *Keeper) TraceEthTxMsg(

return &result, txConfig.LogIndex + uint(len(res.Logs)), nil
}

func (k Keeper) TokenMapping(
goCtx context.Context, req *evm.QueryTokenMappingRequest,
) (*evm.QueryTokenMappingResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// first try lookup by cosmos denom
bankDenomIter := k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, req.Token)
funTokenMappings := k.FunTokens.Collect(ctx, bankDenomIter)
if len(funTokenMappings) > 0 {
// assumes that there is only one mapping for a given denom
return &evm.QueryTokenMappingResponse{
FunToken: &funTokenMappings[0],
}, nil
}

erc20AddrIter := k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, gethcommon.HexToAddress(req.Token))
funTokenMappings = k.FunTokens.Collect(ctx, erc20AddrIter)
if len(funTokenMappings) > 0 {
// assumes that there is only one mapping for a given erc20 address
return &evm.QueryTokenMappingResponse{
FunToken: &funTokenMappings[0],
}, nil
}

return nil, grpcstatus.Errorf(grpccodes.NotFound, "token mapping not found for %s", req.Token)
}
88 changes: 88 additions & 0 deletions x/evm/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -894,3 +894,91 @@ func (s *KeeperSuite) TestTestTraceBlock() {
})
}
}

func (s *KeeperSuite) TestQueryTokenMapping() {
type In = *evm.QueryTokenMappingRequest
type Out = *evm.QueryTokenMappingResponse
testCases := []TestCase[In, Out]{
{
name: "sad: no token mapping",
scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) {
req = &evm.QueryTokenMappingRequest{
Token: "unibi",
}
wantResp = &evm.QueryTokenMappingResponse{
FunToken: nil,
}
return req, wantResp
},
wantErr: "token mapping not found for unibi",
},
{
name: "happy: token mapping exists from cosmos coin -> ERC20 token",
setup: func(deps *evmtest.TestDeps) {
_ = deps.K.FunTokens.SafeInsert(
deps.Ctx,
gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"),
"unibi",
true,
)
},
scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) {
req = &evm.QueryTokenMappingRequest{
Token: "unibi",
}
wantResp = &evm.QueryTokenMappingResponse{
FunToken: &evm.FunToken{
Erc20Addr: "0xAEf9437FF23D48D73271a41a8A094DEc9ac71477",
BankDenom: "unibi",
IsMadeFromCoin: true,
},
}
return req, wantResp
},
wantErr: "",
},
{
name: "happy: token mapping exists from ERC20 token -> cosmos coin",
setup: func(deps *evmtest.TestDeps) {
_ = deps.K.FunTokens.SafeInsert(
deps.Ctx,
gethcommon.HexToAddress("0xAEf9437FF23D48D73271a41a8A094DEc9ac71477"),
"unibi",
true,
)
},
scenario: func(deps *evmtest.TestDeps) (req In, wantResp Out) {
req = &evm.QueryTokenMappingRequest{
Token: "0xAEf9437FF23D48D73271a41a8A094DEc9ac71477",
}
wantResp = &evm.QueryTokenMappingResponse{
FunToken: &evm.FunToken{
Erc20Addr: "0xAEf9437FF23D48D73271a41a8A094DEc9ac71477",
BankDenom: "unibi",
IsMadeFromCoin: true,
},
}
return req, wantResp
},
wantErr: "",
},
}

for _, tc := range testCases {
s.Run(tc.name, func() {
deps := evmtest.NewTestDeps()
if tc.setup != nil {
tc.setup(&deps)
}
req, wantResp := tc.scenario(&deps)
goCtx := sdk.WrapSDKContext(deps.Ctx)
gotResp, err := deps.K.TokenMapping(goCtx, req)
if tc.wantErr != "" {
s.Require().ErrorContains(err, tc.wantErr)
return
}
s.Assert().NoError(err)
s.EqualValues(wantResp, gotResp)
})
}
}
Loading

0 comments on commit 202a3bd

Please sign in to comment.