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: implement nft module msg server #10074

Merged
merged 23 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion client/grpc/tmservice/query.pb.go

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

38 changes: 20 additions & 18 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -3189,7 +3189,7 @@ GetNodeInfoRequest is the request type for the Query/GetNodeInfo RPC method.
<a name="cosmos.base.tendermint.v1beta1.GetNodeInfoResponse"></a>

### GetNodeInfoResponse
GetNodeInfoResponse is the request type for the Query/GetNodeInfo RPC method.
GetNodeInfoResponse is the response type for the Query/GetNodeInfo RPC method.


| Field | Type | Label | Description |
Expand Down Expand Up @@ -7090,11 +7090,12 @@ Class defines the class of the nft type.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `id` | [string](#string) | | |
| `name` | [string](#string) | | |
| `symbol` | [string](#string) | | |
| `description` | [string](#string) | | |
| `uri` | [string](#string) | | |
| `id` | [string](#string) | | id defines the unique identifier of the NFT classification, similar to the contract address of ERC721 |
| `name` | [string](#string) | | name defines the human-readable name of the NFT classification |
| `symbol` | [string](#string) | | symbol is an abbreviated name for nft classification |
| `description` | [string](#string) | | description is a brief description of nft classification |
| `uri` | [string](#string) | | uri is a URI may point to a JSON file that conforms to the nft classification Metadata JSON Schema. |
| `uri_hash` | [string](#string) | | uri_hash is a hash of the document pointed to uri |



Expand All @@ -7104,15 +7105,16 @@ Class defines the class of the nft type.
<a name="cosmos.nft.v1beta1.NFT"></a>

### NFT
NFT defines the nft.
NFT defines the NFT.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `class_id` | [string](#string) | | |
| `id` | [string](#string) | | |
| `uri` | [string](#string) | | |
| `data` | [google.protobuf.Any](#google.protobuf.Any) | | |
| `class_id` | [string](#string) | | class_id defines the unique identifier of the NFT classification, similar to the contract address of ERC721 |
| `id` | [string](#string) | | id defines the unique identification of NFT |
| `uri` | [string](#string) | | uri defines NFT's metadata storage address outside the chain |
| `uri_hash` | [string](#string) | | uri_hash is a hash of the document pointed to uri |
| `data` | [google.protobuf.Any](#google.protobuf.Any) | | data is the metadata of the NFT |



Expand Down Expand Up @@ -7143,8 +7145,8 @@ Entry Defines all nft owned by a person

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `owner` | [string](#string) | | |
| `nfts` | [NFT](#cosmos.nft.v1beta1.NFT) | repeated | |
| `owner` | [string](#string) | | owner is the owner address of the following nft |
| `nfts` | [NFT](#cosmos.nft.v1beta1.NFT) | repeated | nfts is a group of nfts of the same owner |



Expand Down Expand Up @@ -7415,7 +7417,7 @@ Query defines the gRPC querier service.
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Balance` | [QueryBalanceRequest](#cosmos.nft.v1beta1.QueryBalanceRequest) | [QueryBalanceResponse](#cosmos.nft.v1beta1.QueryBalanceResponse) | Balance queries the number of NFTs of a given class owned by the owner, same as balanceOf in ERC721 | GET|/cosmos/nft/v1beta1/balance/{class_id}/{owner}|
| `Owner` | [QueryOwnerRequest](#cosmos.nft.v1beta1.QueryOwnerRequest) | [QueryOwnerResponse](#cosmos.nft.v1beta1.QueryOwnerResponse) | Owner queries the owner of the NFT based on its class and id, same as ownerOf in ERC721 | GET|/cosmos/nft/v1beta1/owner/{class_id}/{id}|
| `Supply` | [QuerySupplyRequest](#cosmos.nft.v1beta1.QuerySupplyRequest) | [QuerySupplyResponse](#cosmos.nft.v1beta1.QuerySupplyResponse) | Supply queries the number of nft based on the class, same as totalSupply of ERC721 | GET|/cosmos/nft/v1beta1/supply/{class_id}|
| `Supply` | [QuerySupplyRequest](#cosmos.nft.v1beta1.QuerySupplyRequest) | [QuerySupplyResponse](#cosmos.nft.v1beta1.QuerySupplyResponse) | Supply queries the number of NFTs from the given class, same as totalSupply of ERC721. | GET|/cosmos/nft/v1beta1/supply/{class_id}|
| `NFTsOfClass` | [QueryNFTsOfClassRequest](#cosmos.nft.v1beta1.QueryNFTsOfClassRequest) | [QueryNFTsOfClassResponse](#cosmos.nft.v1beta1.QueryNFTsOfClassResponse) | NFTsOfClass queries all NFTs of a given class or optional owner, similar to tokenByIndex in ERC721Enumerable | GET|/cosmos/nft/v1beta1/nfts/{class_id}|
| `NFT` | [QueryNFTRequest](#cosmos.nft.v1beta1.QueryNFTRequest) | [QueryNFTResponse](#cosmos.nft.v1beta1.QueryNFTResponse) | NFT queries an NFT based on its class and id. | GET|/cosmos/nft/v1beta1/nfts/{class_id}/{id}|
| `Class` | [QueryClassRequest](#cosmos.nft.v1beta1.QueryClassRequest) | [QueryClassResponse](#cosmos.nft.v1beta1.QueryClassResponse) | Class queries an NFT class based on its id | GET|/cosmos/nft/v1beta1/classes/{class_id}|
Expand All @@ -7440,10 +7442,10 @@ MsgSend represents a message to send a nft from one account to another account.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `class_id` | [string](#string) | | |
| `id` | [string](#string) | | |
| `sender` | [string](#string) | | |
| `receiver` | [string](#string) | | |
| `class_id` | [string](#string) | | class_id defines the unique identifier of the nft classification, similar to the contract address of ERC721 |
| `id` | [string](#string) | | id defines the unique identification of nft |
| `sender` | [string](#string) | | sender is the address of the owner of nft |
| `receiver` | [string](#string) | | receiver is the receiver address of nft |



Expand Down
12 changes: 10 additions & 2 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ import (
"github.com/cosmos/cosmos-sdk/x/mint"
mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
"github.com/cosmos/cosmos-sdk/x/nft"
nftkeeper "github.com/cosmos/cosmos-sdk/x/nft/keeper"
nftmodule "github.com/cosmos/cosmos-sdk/x/nft/module"
"github.com/cosmos/cosmos-sdk/x/params"
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
Expand Down Expand Up @@ -116,6 +119,7 @@ var (
evidence.AppModuleBasic{},
authzmodule.AppModuleBasic{},
vesting.AppModuleBasic{},
nftmodule.AppModuleBasic{},
)

// module account permissions
Expand All @@ -126,6 +130,7 @@ var (
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
govtypes.ModuleName: {authtypes.Burner},
nft.ModuleName: nil,
}
)

Expand Down Expand Up @@ -167,6 +172,7 @@ type SimApp struct {
AuthzKeeper authzkeeper.Keeper
EvidenceKeeper evidencekeeper.Keeper
FeeGrantKeeper feegrantkeeper.Keeper
NFTKeeper nftkeeper.Keeper

// the module manager
mm *module.Manager
Expand Down Expand Up @@ -208,7 +214,7 @@ func NewSimApp(
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey,
evidencetypes.StoreKey, capabilitytypes.StoreKey,
authzkeeper.StoreKey,
authzkeeper.StoreKey, nftkeeper.StoreKey,
)
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
// NOTE: The testingkey is just mounted for testing purposes. Actual applications should
Expand Down Expand Up @@ -290,6 +296,7 @@ func NewSimApp(
// register the governance hooks
),
)
app.NFTKeeper = nftkeeper.NewKeeper(keys[nftkeeper.StoreKey], appCodec, app.AccountKeeper, app.BankKeeper)

// create evidence keeper with router
evidenceKeeper := evidencekeeper.NewKeeper(
Expand Down Expand Up @@ -326,6 +333,7 @@ func NewSimApp(
evidence.NewAppModule(app.EvidenceKeeper),
params.NewAppModule(app.ParamsKeeper),
authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
)

// During begin block slashing happens after distr.BeginBlocker so that
Expand All @@ -348,7 +356,7 @@ func NewSimApp(
capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName,
slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName,
genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName,
feegrant.ModuleName,
feegrant.ModuleName, nft.ModuleName,
)

app.mm.RegisterInvariants(&app.CrisisKeeper)
Expand Down
14 changes: 14 additions & 0 deletions x/nft/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package nft

import (
types "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
)

func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgSend{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}
15 changes: 15 additions & 0 deletions x/nft/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package nft

import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// x/nft module sentinel errors
var (
ErrInvalidNFT = sdkerrors.Register(ModuleName, 2, "invalid nft")
ErrClassExists = sdkerrors.Register(ModuleName, 3, "nft class already exist")
ErrClassNotExists = sdkerrors.Register(ModuleName, 4, "nft class does not exist")
ErrNFTExists = sdkerrors.Register(ModuleName, 5, "nft already exist")
ErrNFTNotExists = sdkerrors.Register(ModuleName, 6, "nft does not exist")
ErrInvalidID = sdkerrors.Register(ModuleName, 7, "invalid id")
)
18 changes: 18 additions & 0 deletions x/nft/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nft

import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

// BankKeeper defines the contract needed to be fulfilled for banking and supply
// dependencies.
type BankKeeper interface {
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
}

// AccountKeeper defines the contract required for account APIs.
type AccountKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to depend on Query proto services rather than keepers directly, but I think we will firstly need to clean other parts of SDK before requesting that changes (and finalize adr-33).

23 changes: 23 additions & 0 deletions x/nft/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package nft

// ValidateGenesis check the given genesis state has no integrity issues
func ValidateGenesis(data GenesisState) error {
for _, class := range data.Classes {
if err := ValidateClassID(class.Id); err != nil {
panic(err)
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
}
}
for _, entry := range data.Entries {
for _, nft := range entry.Nfts {
if err := ValidateNFTID(nft.Id); err != nil {
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
panic(err)
}
}
}
return nil
}

// DefaultGenesisState - Return a default genesis state
func DefaultGenesisState() *GenesisState {
return &GenesisState{}
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 3 additions & 1 deletion x/nft/genesis.pb.go

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

61 changes: 61 additions & 0 deletions x/nft/keeper/class.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/nft"
)

// NewClass defines a method for creating a new nft class
func (k Keeper) NewClass(ctx sdk.Context, class nft.Class) error {
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
if k.HasClass(ctx, class.Id) {
return sdkerrors.Wrap(nft.ErrClassExists, class.Id)
}
bz := k.cdc.MustMarshal(&class)
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
store := ctx.KVStore(k.storeKey)
store.Set(classStoreKey(class.Id), bz)
return nil
}

// UpdateClass defines a method for updating a exist nft class
func (k Keeper) UpdateClass(ctx sdk.Context, class nft.Class) error {
if !k.HasClass(ctx, class.Id) {
return sdkerrors.Wrap(nft.ErrClassNotExists, class.Id)
}
bz := k.cdc.MustMarshal(&class)
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
store := ctx.KVStore(k.storeKey)
store.Set(classStoreKey(class.Id), bz)
return nil
}

// GetClass defines a method for returning the class information of the specified id
func (k Keeper) GetClass(ctx sdk.Context, classID string) (nft.Class, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(classStoreKey(classID))

var class nft.Class
if len(bz) == 0 {
return class, false
}
k.cdc.MustUnmarshal(bz, &class)
return class, true
}

// GetClasses defines a method for returning all classes information
func (k Keeper) GetClasses(ctx sdk.Context) (classes []*nft.Class) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ClassKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var class nft.Class
k.cdc.MustUnmarshal(iterator.Value(), &class)
classes = append(classes, &class)
}
return
}

// HasClass determines whether the specified classID exist
func (k Keeper) HasClass(ctx sdk.Context, classID string) bool {
store := ctx.KVStore(k.storeKey)
return store.Has(classStoreKey(classID))
}
57 changes: 57 additions & 0 deletions x/nft/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package keeper

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/nft"
)

// InitGenesis new nft genesis
func (k Keeper) InitGenesis(ctx sdk.Context, data *nft.GenesisState) {
for _, class := range data.Classes {
if err := k.NewClass(ctx, *class); err != nil {
panic(err)
}

}
for _, entry := range data.Entries {
for _, nft := range entry.Nfts {
owner, err := sdk.AccAddressFromBech32(entry.Owner)
if err != nil {
panic(err)
}

if err := k.Mint(ctx, *nft, owner); err != nil {
panic(err)
}
}
}
}

// ExportGenesis returns a GenesisState for a given context.
func (k Keeper) ExportGenesis(ctx sdk.Context) *nft.GenesisState {
classes := k.GetClasses(ctx)
nftMap := make(map[string][]*nft.NFT)
for _, class := range classes {
nfts := k.GetNFTsOfClass(ctx, class.Id)
for i, n := range nfts {
owner := k.GetOwner(ctx, n.ClassId, n.Id)
nftArr, ok := nftMap[owner.String()]
if !ok {
nftArr = make([]*nft.NFT, 0)
}
nftMap[owner.String()] = append(nftArr, &nfts[i])
}
}

entries := make([]*nft.Entry, 0, len(nftMap))
for k, v := range nftMap {
dreamer-zq marked this conversation as resolved.
Show resolved Hide resolved
entries = append(entries, &nft.Entry{
Owner: k,
Nfts: v,
})
}
return &nft.GenesisState{
Classes: classes,
Entries: entries,
}
}
30 changes: 30 additions & 0 deletions x/nft/keeper/keeper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package keeper

import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/nft"
)

// Keeper of the nft store
type Keeper struct {
cdc codec.BinaryCodec
storeKey sdk.StoreKey
bk nft.BankKeeper
}

// NewKeeper creates a new nft Keeper instance
func NewKeeper(key sdk.StoreKey,
cdc codec.BinaryCodec, ak nft.AccountKeeper, bk nft.BankKeeper,
) Keeper {
// ensure nft module account is set
if addr := ak.GetModuleAddress(nft.ModuleName); addr == nil {
panic("the nft module account has not been set")
}

return Keeper{
cdc: cdc,
storeKey: key,
bk: bk,
}
}
Loading