feat: implement nft module query server (#10462)

<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

QueryServer implementation and corresponding keeper methods,refer #9826

<!-- Add a description of the changes that this PR introduces and the files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Zhiqiang Zhang 2021-11-07 07:05:45 +08:00 committed by GitHub
parent d83a3bf92c
commit 4e718a8541
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 790 additions and 2 deletions

View File

@ -12,4 +12,5 @@ var (
ErrNFTExists = sdkerrors.Register(ModuleName, 5, "nft already exist")
ErrNFTNotExists = sdkerrors.Register(ModuleName, 6, "nft does not exist")
ErrInvalidID = sdkerrors.Register(ModuleName, 7, "invalid id")
ErrInvalidClassID = sdkerrors.Register(ModuleName, 8, "invalid class id")
)

192
x/nft/keeper/grpc_query.go Normal file
View File

@ -0,0 +1,192 @@
package keeper
import (
"context"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/nft"
)
var _ nft.QueryServer = Keeper{}
// Balance return the number of NFTs of a given class owned by the owner, same as balanceOf in ERC721
func (k Keeper) Balance(goCtx context.Context, r *nft.QueryBalanceRequest) (*nft.QueryBalanceResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
owner, err := sdk.AccAddressFromBech32(r.Owner)
if err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
balance := k.GetBalance(ctx, r.ClassId, owner)
return &nft.QueryBalanceResponse{Amount: balance}, nil
}
// Owner return the owner of the NFT based on its class and id, same as ownerOf in ERC721
func (k Keeper) Owner(goCtx context.Context, r *nft.QueryOwnerRequest) (*nft.QueryOwnerResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
if err := nft.ValidateNFTID(r.Id); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
owner := k.GetOwner(ctx, r.ClassId, r.Id)
return &nft.QueryOwnerResponse{Owner: owner.String()}, nil
}
// Supply return the number of NFTs from the given class, same as totalSupply of ERC721.
func (k Keeper) Supply(goCtx context.Context, r *nft.QuerySupplyRequest) (*nft.QuerySupplyResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
supply := k.GetTotalSupply(ctx, r.ClassId)
return &nft.QuerySupplyResponse{Amount: supply}, nil
}
// NFTsOfClass return all NFTs of a given class or optional owner, similar to tokenByIndex in ERC721Enumerable
func (k Keeper) NFTsOfClass(goCtx context.Context, r *nft.QueryNFTsOfClassRequest) (*nft.QueryNFTsOfClassResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
var nfts []*nft.NFT
ctx := sdk.UnwrapSDKContext(goCtx)
// if owner is not empty, filter nft by owner
if len(r.Owner) > 0 {
owner, err := sdk.AccAddressFromBech32(r.Owner)
if err != nil {
return nil, err
}
ownerStore := k.getClassStoreByOwner(ctx, owner, r.ClassId)
pageRes, err := query.Paginate(ownerStore, r.Pagination, func(key []byte, _ []byte) error {
nft, has := k.GetNFT(ctx, r.ClassId, string(key))
if has {
nfts = append(nfts, &nft)
}
return nil
})
if err != nil {
return nil, err
}
return &nft.QueryNFTsOfClassResponse{
Nfts: nfts,
Pagination: pageRes,
}, nil
}
nftStore := k.getNFTStore(ctx, r.ClassId)
pageRes, err := query.Paginate(nftStore, r.Pagination, func(_ []byte, value []byte) error {
var nft nft.NFT
if err := k.cdc.Unmarshal(value, &nft); err != nil {
return err
}
nfts = append(nfts, &nft)
return nil
})
if err != nil {
return nil, err
}
return &nft.QueryNFTsOfClassResponse{
Nfts: nfts,
Pagination: pageRes,
}, nil
}
// NFT return an NFT based on its class and id.
func (k Keeper) NFT(goCtx context.Context, r *nft.QueryNFTRequest) (*nft.QueryNFTResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
if err := nft.ValidateNFTID(r.Id); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
n, has := k.GetNFT(ctx, r.ClassId, r.Id)
if !has {
return nil, sdkerrors.Wrapf(nft.ErrNFTNotExists, "not found nft: class: %s, id: %s", r.ClassId, r.Id)
}
return &nft.QueryNFTResponse{Nft: &n}, nil
}
// Class return an NFT class based on its id
func (k Keeper) Class(goCtx context.Context, r *nft.QueryClassRequest) (*nft.QueryClassResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
if err := nft.ValidateClassID(r.ClassId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
class, has := k.GetClass(ctx, r.ClassId)
if !has {
return nil, sdkerrors.Wrapf(nft.ErrClassNotExists, "not found class: %s", r.ClassId)
}
return &nft.QueryClassResponse{Class: &class}, nil
}
// Classes return all NFT classes
func (k Keeper) Classes(goCtx context.Context, r *nft.QueryClassesRequest) (*nft.QueryClassesResponse, error) {
if r == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "empty request")
}
ctx := sdk.UnwrapSDKContext(goCtx)
store := ctx.KVStore(k.storeKey)
classStore := prefix.NewStore(store, ClassKey)
var classes []*nft.Class
pageRes, err := query.Paginate(classStore, r.Pagination, func(_ []byte, value []byte) error {
var class nft.Class
if err := k.cdc.Unmarshal(value, &class); err != nil {
return err
}
classes = append(classes, &class)
return nil
})
if err != nil {
return nil, err
}
return &nft.QueryClassesResponse{
Classes: classes,
Pagination: pageRes,
}, nil
}

View File

@ -0,0 +1,588 @@
package keeper_test
import (
gocontext "context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/cosmos/cosmos-sdk/x/nft"
)
func TestGRPCQuery(t *testing.T) {
suite.Run(t, new(TestSuite))
}
func (suite *TestSuite) TestBalance() {
var (
req *nft.QueryBalanceRequest
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
balance uint64
postTest func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryBalanceRequest{}
},
"invalid class id",
0,
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {},
},
{
"fail invalid Owner addr",
func(index int, require *require.Assertions) {
req = &nft.QueryBalanceRequest{
ClassId: testClassID,
Owner: "owner",
}
},
"decoding bech32 failed",
0,
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {},
},
{
"Success",
func(index int, require *require.Assertions) {
suite.TestMint()
req = &nft.QueryBalanceRequest{
ClassId: testClassID,
Owner: suite.addrs[0].String(),
}
},
"",
1,
func(index int, require *require.Assertions, res *nft.QueryBalanceResponse, expBalance uint64) {
require.Equal(res.Amount, expBalance, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Balance(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result, tc.balance)
})
}
}
func (suite *TestSuite) TestOwner() {
var (
req *nft.QueryOwnerRequest
owner string
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryOwnerResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
Id: testID,
}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {},
},
{
"fail empty nft id",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
}
},
"invalid nft id",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {},
},
{
"success but nft id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
Id: "kitty2",
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
{
"success but class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryOwnerRequest{
ClassId: "kitty1",
Id: testID,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
{
"Success",
func(index int, require *require.Assertions) {
suite.TestMint()
req = &nft.QueryOwnerRequest{
ClassId: testClassID,
Id: testID,
}
owner = suite.addrs[0].String()
},
"",
func(index int, require *require.Assertions, res *nft.QueryOwnerResponse) {
require.Equal(res.Owner, owner, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Owner(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestSupply() {
var (
req *nft.QuerySupplyRequest
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
supply uint64
postTest func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{}
},
"invalid class id",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {},
},
{
"success but class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{
ClassId: "kitty1",
}
},
"",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
{
"success but supply equal zero",
func(index int, require *require.Assertions) {
req = &nft.QuerySupplyRequest{
ClassId: testClassID,
}
suite.TestSaveClass()
},
"",
0,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
{
"Success",
func(index int, require *require.Assertions) {
n := nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
err := suite.app.NFTKeeper.Mint(suite.ctx, n, suite.addrs[0])
require.NoError(err, "the error occurred on:%d", index)
req = &nft.QuerySupplyRequest{
ClassId: testClassID,
}
},
"",
1,
func(index int, require *require.Assertions, res *nft.QuerySupplyResponse, supply uint64) {
require.Equal(res.Amount, supply, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Supply(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result, tc.supply)
})
}
}
func (suite *TestSuite) TestNFTsOfClass() {
var (
req *nft.QueryNFTsOfClassRequest
nfts []*nft.NFT
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {},
},
{
"success, no nft",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
}
suite.TestSaveClass()
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"success, class id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: "kitty1",
}
n := nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
err := suite.app.NFTKeeper.Mint(suite.ctx, n, suite.addrs[0])
require.NoError(err, "the error occurred on:%d", index)
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"success, owner not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
Owner: suite.addrs[1].String(),
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Len(res.Nfts, 0, "the error occurred on:%d", index)
},
},
{
"Success, query by classId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
}
nfts = []*nft.NFT{
{
ClassId: testClassID,
Id: testID,
Uri: testURI,
},
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Equal(res.Nfts, nfts, "the error occurred on:%d", index)
},
},
{
"Success,query by classId and owner",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTsOfClassRequest{
ClassId: testClassID,
Owner: suite.addrs[0].String(),
}
nfts = []*nft.NFT{
{
ClassId: testClassID,
Id: testID,
Uri: testURI,
},
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTsOfClassResponse) {
require.Equal(res.Nfts, nfts, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.NFTsOfClass(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestNFT() {
var (
req *nft.QueryNFTRequest
expNFT nft.NFT
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryNFTResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail empty nft id",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
}
},
"invalid nft id",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail ClassId not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: "kitty1",
Id: testID,
}
suite.TestMint()
},
"not found nft",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"fail nft id not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
Id: "kitty2",
}
},
"not found nft",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {},
},
{
"success",
func(index int, require *require.Assertions) {
req = &nft.QueryNFTRequest{
ClassId: testClassID,
Id: testID,
}
expNFT = nft.NFT{
ClassId: testClassID,
Id: testID,
Uri: testURI,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryNFTResponse) {
require.Equal(*res.Nft, expNFT, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.NFT(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestClass() {
var (
req *nft.QueryClassRequest
class nft.Class
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryClassResponse)
}{
{
"fail empty ClassId",
func(index int, require *require.Assertions) {
req = &nft.QueryClassRequest{}
},
"invalid class id",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {},
},
{
"fail ClassId not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryClassRequest{
ClassId: "kitty1",
}
suite.TestSaveClass()
},
"not found class",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {},
},
{
"success",
func(index int, require *require.Assertions) {
class = nft.Class{
Id: testClassID,
Name: testClassName,
Symbol: testClassSymbol,
Description: testClassDescription,
Uri: testClassURI,
UriHash: testClassURIHash,
}
req = &nft.QueryClassRequest{
ClassId: testClassID,
}
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassResponse) {
require.Equal(*res.Class, class, "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Class(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}
func (suite *TestSuite) TestClasses() {
var (
req *nft.QueryClassesRequest
classes []nft.Class
)
testCases := []struct {
msg string
malleate func(index int, require *require.Assertions)
expError string
postTest func(index int, require *require.Assertions, res *nft.QueryClassesResponse)
}{
{
"success Class not exist",
func(index int, require *require.Assertions) {
req = &nft.QueryClassesRequest{}
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassesResponse) {
require.Len(res.Classes, 0)
},
},
{
"success",
func(index int, require *require.Assertions) {
req = &nft.QueryClassesRequest{}
classes = []nft.Class{
{
Id: testClassID,
Name: testClassName,
Symbol: testClassSymbol,
Description: testClassDescription,
Uri: testClassURI,
UriHash: testClassURIHash,
},
}
suite.TestSaveClass()
},
"",
func(index int, require *require.Assertions, res *nft.QueryClassesResponse) {
require.Len(res.Classes, 1, "the error occurred on:%d", index)
require.Equal(*res.Classes[0], classes[0], "the error occurred on:%d", index)
},
},
}
for index, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
require := suite.Require()
tc.malleate(index, require)
result, err := suite.queryClient.Classes(gocontext.Background(), req)
if tc.expError == "" {
require.NoError(err)
} else {
require.Error(err)
require.Contains(err.Error(), tc.expError)
}
tc.postTest(index, require, result)
})
}
}

View File

@ -7,6 +7,7 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/nft"
@ -37,9 +38,13 @@ func (s *TestSuite) SetupTest() {
app := simapp.Setup(s.T(), false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
ctx = ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()})
queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
nft.RegisterQueryServer(queryHelper, app.NFTKeeper)
queryClient := nft.NewQueryClient(queryHelper)
s.app = app
s.ctx = ctx
s.queryClient = queryClient
s.addrs = simapp.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(30000000))
}

View File

@ -3,6 +3,8 @@ package nft
import (
fmt "fmt"
"regexp"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
var (
@ -19,7 +21,7 @@ var (
// ValidateClassID returns whether the class id is valid
func ValidateClassID(id string) error {
if !reClassID.MatchString(id) {
return fmt.Errorf("invalid class id: %s", id)
return sdkerrors.Wrapf(ErrInvalidClassID, "invalid class id: %s", id)
}
return nil
}
@ -27,7 +29,7 @@ func ValidateClassID(id string) error {
// ValidateNFTID returns whether the nft id is valid
func ValidateNFTID(id string) error {
if !reNFTID.MatchString(id) {
return fmt.Errorf("invalid nft id: %s", id)
return sdkerrors.Wrapf(ErrInvalidID, "invalid nft id: %s", id)
}
return nil
}