Skip to content

Commit

Permalink
Add AllDenomMetadata BankQuery (#1426)
Browse files Browse the repository at this point in the history
* x/wasm: add AllDenomMetadata BankQuery

* x/wasm: fix AllDenomMetadata BankQuery to have pagination and add DenomMetadata BankQuery

* Use simplified pagination

* Fix request conversion

* Add unknown denom test cases

* Add test for pagination conversion

* Fix nits

* Use wasmvm 1.3.0-rc.0

* Fix test

---------

Co-authored-by: Nikhil Suri <[email protected]>
  • Loading branch information
chipshort and nik-suri authored Jul 6, 2023
1 parent 17e14a4 commit d2e9ace
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 5 deletions.
1 change: 1 addition & 0 deletions app/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ func AllCapabilities() []string {
"stargate",
"cosmwasm_1_1",
"cosmwasm_1_2",
"cosmwasm_1_3",
}
}
71 changes: 69 additions & 2 deletions x/wasm/keeper/query_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import (
"fmt"

errorsmod "cosmossdk.io/errors"
"github.com/CosmWasm/wasmd/x/wasm/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"

"github.com/CosmWasm/wasmd/x/wasm/types"
)

type QueryHandler struct {
Expand Down Expand Up @@ -201,6 +202,27 @@ func BankQuerier(bankKeeper types.BankViewKeeper) func(ctx sdk.Context, request
}
return json.Marshal(res)
}
if request.DenomMetadata != nil {
denomMetadata, ok := bankKeeper.GetDenomMetaData(ctx, request.DenomMetadata.Denom)
if !ok {
return nil, errorsmod.Wrap(sdkerrors.ErrNotFound, request.DenomMetadata.Denom)
}
res := wasmvmtypes.DenomMetadataResponse{
Metadata: ConvertSdkDenomMetadataToWasmDenomMetadata(denomMetadata),
}
return json.Marshal(res)
}
if request.AllDenomMetadata != nil {
bankQueryRes, err := bankKeeper.DenomsMetadata(ctx, ConvertToDenomsMetadataRequest(request.AllDenomMetadata))
if err != nil {
return nil, sdkerrors.ErrInvalidRequest
}
res := wasmvmtypes.AllDenomMetadataResponse{
Metadata: ConvertSdkDenomMetadatasToWasmDenomMetadatas(bankQueryRes.Metadatas),
NextKey: bankQueryRes.Pagination.NextKey,
}
return json.Marshal(res)
}
return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown BankQuery variant"}
}
}
Expand Down Expand Up @@ -584,6 +606,51 @@ func ConvertSdkCoinToWasmCoin(coin sdk.Coin) wasmvmtypes.Coin {
}
}

func ConvertToDenomsMetadataRequest(wasmRequest *wasmvmtypes.AllDenomMetadataQuery) *banktypes.QueryDenomsMetadataRequest {
ret := &banktypes.QueryDenomsMetadataRequest{}
if wasmRequest.Pagination != nil {
ret.Pagination = &query.PageRequest{
Key: wasmRequest.Pagination.Key,
Limit: uint64(wasmRequest.Pagination.Limit),
Reverse: wasmRequest.Pagination.Reverse,
}
}
return ret
}

func ConvertSdkDenomMetadatasToWasmDenomMetadatas(metadata []banktypes.Metadata) []wasmvmtypes.DenomMetadata {
converted := make([]wasmvmtypes.DenomMetadata, len(metadata))
for i, m := range metadata {
converted[i] = ConvertSdkDenomMetadataToWasmDenomMetadata(m)
}
return converted
}

func ConvertSdkDenomMetadataToWasmDenomMetadata(metadata banktypes.Metadata) wasmvmtypes.DenomMetadata {
return wasmvmtypes.DenomMetadata{
Description: metadata.Description,
DenomUnits: ConvertSdkDenomUnitsToWasmDenomUnits(metadata.DenomUnits),
Base: metadata.Base,
Display: metadata.Display,
Name: metadata.Name,
Symbol: metadata.Symbol,
URI: metadata.URI,
URIHash: metadata.URIHash,
}
}

func ConvertSdkDenomUnitsToWasmDenomUnits(denomUnits []*banktypes.DenomUnit) []wasmvmtypes.DenomUnit {
converted := make([]wasmvmtypes.DenomUnit, len(denomUnits))
for i, u := range denomUnits {
converted[i] = wasmvmtypes.DenomUnit{
Denom: u.Denom,
Exponent: u.Exponent,
Aliases: u.Aliases,
}
}
return converted
}

// ConvertProtoToJSONMarshal unmarshals the given bytes into a proto message and then marshals it to json.
// This is done so that clients calling stargate queries do not need to define their own proto unmarshalers,
// being able to use response directly by json marshalling, which is supported in cosmwasm.
Expand Down
150 changes: 147 additions & 3 deletions x/wasm/keeper/query_plugins_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -356,6 +357,133 @@ func TestBankQuerierBalance(t *testing.T) {
assert.Equal(t, exp, got)
}

func TestBankQuerierMetadata(t *testing.T) {
metadata := banktypes.Metadata{
Name: "Test Token",
Base: "utest",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
}

mock := bankKeeperMock{GetDenomMetadataFn: func(ctx sdk.Context, denom string) (banktypes.Metadata, bool) {
if denom == "utest" {
return metadata, true
} else {
return banktypes.Metadata{}, false
}
}}

ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
DenomMetadata: &wasmvmtypes.DenomMetadataQuery{
Denom: "utest",
},
})
require.NoError(t, gotErr)
var got wasmvmtypes.DenomMetadataResponse
require.NoError(t, json.Unmarshal(gotBz, &got))
exp := wasmvmtypes.DenomMetadata{
Name: "Test Token",
Base: "utest",
DenomUnits: []wasmvmtypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
}
assert.Equal(t, exp, got.Metadata)

_, gotErr2 := q(ctx, &wasmvmtypes.BankQuery{
DenomMetadata: &wasmvmtypes.DenomMetadataQuery{
Denom: "uatom",
},
})
require.Error(t, gotErr2)
assert.Contains(t, gotErr2.Error(), "uatom: not found")
}

func TestBankQuerierAllMetadata(t *testing.T) {
metadata := []banktypes.Metadata{
{
Name: "Test Token",
Base: "utest",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
},
}

mock := bankKeeperMock{GetDenomsMetadataFn: func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
return &banktypes.QueryDenomsMetadataResponse{
Metadatas: metadata,
Pagination: &query.PageResponse{},
}, nil
}}

ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
gotBz, gotErr := q(ctx, &wasmvmtypes.BankQuery{
AllDenomMetadata: &wasmvmtypes.AllDenomMetadataQuery{},
})
require.NoError(t, gotErr)
var got wasmvmtypes.AllDenomMetadataResponse
require.NoError(t, json.Unmarshal(gotBz, &got))
exp := wasmvmtypes.AllDenomMetadataResponse{
Metadata: []wasmvmtypes.DenomMetadata{
{
Name: "Test Token",
Base: "utest",
DenomUnits: []wasmvmtypes.DenomUnit{
{
Denom: "utest",
Exponent: 0,
},
},
},
},
}
assert.Equal(t, exp, got)
}

func TestBankQuerierAllMetadataPagination(t *testing.T) {
var capturedPagination *query.PageRequest
mock := bankKeeperMock{GetDenomsMetadataFn: func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
capturedPagination = req.Pagination
return &banktypes.QueryDenomsMetadataResponse{
Metadatas: []banktypes.Metadata{},
Pagination: &query.PageResponse{
NextKey: nil,
},
}, nil
}}

ctx := sdk.Context{}
q := keeper.BankQuerier(mock)
_, gotErr := q(ctx, &wasmvmtypes.BankQuery{
AllDenomMetadata: &wasmvmtypes.AllDenomMetadataQuery{
Pagination: &wasmvmtypes.PageRequest{
Key: []byte("key"),
Limit: 10,
},
},
})
require.NoError(t, gotErr)
exp := &query.PageRequest{
Key: []byte("key"),
Limit: 10,
}
assert.Equal(t, exp, capturedPagination)
}

func TestContractInfoWasmQuerier(t *testing.T) {
myValidContractAddr := keeper.RandomBech32AccountAddress(t)
myCreatorAddr := keeper.RandomBech32AccountAddress(t)
Expand Down Expand Up @@ -673,9 +801,11 @@ func (m mockWasmQueryKeeper) GetCodeInfo(ctx sdk.Context, codeID uint64) *types.
}

type bankKeeperMock struct {
GetSupplyFn func(ctx sdk.Context, denom string) sdk.Coin
GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetSupplyFn func(ctx sdk.Context, denom string) sdk.Coin
GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetDenomMetadataFn func(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
GetDenomsMetadataFn func(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error)
}

func (m bankKeeperMock) GetSupply(ctx sdk.Context, denom string) sdk.Coin {
Expand All @@ -699,6 +829,20 @@ func (m bankKeeperMock) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk
return m.GetAllBalancesFn(ctx, addr)
}

func (m bankKeeperMock) GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool) {
if m.GetDenomMetadataFn == nil {
panic("not expected to be called")
}
return m.GetDenomMetadataFn(ctx, denom)
}

func (m bankKeeperMock) DenomsMetadata(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error) {
if m.GetDenomsMetadataFn == nil {
panic("not expected to be called")
}
return m.GetDenomsMetadataFn(ctx, req)
}

func TestConvertProtoToJSONMarshal(t *testing.T) {
testCases := []struct {
name string
Expand Down
3 changes: 3 additions & 0 deletions x/wasm/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
Expand All @@ -20,6 +21,8 @@ type BankViewKeeper interface {
GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
GetSupply(ctx sdk.Context, denom string) sdk.Coin
GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
DenomsMetadata(ctx context.Context, req *banktypes.QueryDenomsMetadataRequest) (*banktypes.QueryDenomsMetadataResponse, error)
}

// Burner is a subset of the sdk bank keeper methods
Expand Down

0 comments on commit d2e9ace

Please sign in to comment.