store nft in bank module

This commit is contained in:
chengwenxi 2021-06-15 19:39:52 +08:00
parent f1e81fa1db
commit 899e2a27bc
1 changed files with 90 additions and 70 deletions

View File

@ -13,9 +13,11 @@ DRAFT
This ADR defines a generic NFT module of `x/nft` which supports NFTs as a `proto.Any` and contains `NFT` as the default implementation.
## Context
NFTs are more digital assets than only crypto arts, which is very helpful for accruing value to ATOM. As a result, Cosmos Hub should implement NFT functions and enable a unified mechanism for storing and sending the ownership representative of NFTs as discussed in https://github.com/cosmos/cosmos-sdk/discussions/9065.
As was discussed in [#9065](https://github.com/cosmos/cosmos-sdk/discussions/9065), several potential solutions can be considered:
- irismod/nft and modules/incubator/nft
- CW721
- DID NFTs
@ -37,73 +39,79 @@ We will create a module `x/nft`, which provides the most basic storage, query fu
### Types
We define a general NFT model and `x/nft` module only stores NFTs by id and owner.
We define a general NFT model and `x/nft` module only stores NFTs by denom.
```protobuf
message NFT {
string id = 1;
string denom = 2;
google.protobuf.Any data = 3;
}
```
```go
type NFT struct{
string id = 1;
string owner = 2;
Data data = 3;
}
// Data defines a minimum funtion set consistent with `ERC721Metadata` interface.
type Data interface {
Name() string // A descriptive name for a collection of NFTs
Symbol() string // An abbreviated name for NFTs
URI() string // A distinct Uniform Resource Identifier (URI) for a given asset
type Metadata struct {
string Name // A descriptive name for a collection of NFTs
string Symbol // An abbreviated name for NFTs
string URI // A distinct Uniform Resource Identifier (URI) for a given asset
sdktypes.Any data; // NFT specific data
}
```
The NFT conforms to the following specifications:
* The Id is an immutable field used as a unique identifier. NFT identifiers don't currently have a naming convention but can be used in association with existing Hub attributes, e.g., defining an NFT's identifier as an immutable Hub address allows its integration into existing Hub account management modules.
- The Id is an immutable field used as a unique identifier. NFT identifiers don't currently have a naming convention but can be used in association with existing Hub attributes, e.g., defining an NFT's identifier as an immutable Hub address allows its integration into existing Hub account management modules.
We envision that identifiers can accommodate mint and transfer actions.
The Id is also the primary index for storing NFTs.
```
id (string) --> NFT (bytes)
```
* Owner: This mutable field represents the current account owner (on the Hub) of the NFT, i.e., the account that will have authorization
to perform various activities in the future. This can be a user, a module account, or potentially a future NFT module that adds capabilities.
Owner is also the secondary index for storing NFT IDs owned by an address
```
owner (address) --> []string
```
* Data: This mutable field allows attaching special information to the NFT, for example:
* metadata such as title of the work and URI
* immutable data and parameters (such actual NFT data, hash or seed for generators)
* mutable data and parameters that change when transferring or when certain criteria are met (such as provenance)
This ADR doesn't specify values that this field can take; however, best practices recommend upper-level NFT modules clearly specify its contents.
Although the value of this field doesn't provide additional context required to manage NFT records, which means that the field can technically be removed from the specification,
the field's existence allows basic informational/UI functionality.
The Id is also the primary index for storing NFTs.
```
id (string) --> NFT (bytes)
```
- Owner: This mutable field represents the current account owner (on the Hub) of the NFT, i.e., the account that will have the authorization to perform various activities in the future. This can be a user, a module account, or potentially a future NFT module that adds capabilities.
Owner is also the secondary index for storing NFT IDs owned by an address
```
owner (address) --> []string
```
- Data: This mutable field allows attaching special information to the NFT, for example:
- metadata such as the title of the work and URI
- immutable data and parameters (such actual NFT data, hash or seed for generators)
- mutable data and parameters that change when transferring or when certain criteria are met (such as provenance)
This ADR doesn't specify values that this field can take; however, best practices recommend upper-level NFT modules clearly specify their contents.
Although the value of this field doesn't provide the additional context required to manage NFT records, which means that the field can technically be removed from the specification,
the field's existence allows basic informational/UI functionality.
### `Msg` Service
```protobuf
service Msg {
rpc Mint(MsgMint) returns (MsgMintResponse);
rpc TransferOwnership(MsgTransferOwnership) returns (MsgTransferOwnershipResponse);
rpc Send(MsgSend) returns (MsgTransferOwnershipResponse);
rpc Burn(MsgBurn) returns (MsgBurnResponse);
}
message MsgMint {
string id = 1;
string owner = 2;
google.protobuf.Any data = 3;
string minter = 4;
string denom = 1;
Metadata metadata = 2;
string owner = 3;
}
message MsgMintResponse {
string id = 1;
}
message MsgMintResponse {}
message MsgTransferOwnership {
message MsgSend {
string id = 1;
string sender = 2;
string reveiver = 3;
google.protobuf.Any data = 4;
google.protobuf.Any Data = 4;
}
message MsgTransferOwnershipResponse {}
message MsgSendResponse {}
message MsgBurn {
string id = 1;
@ -118,7 +126,7 @@ message MsgBurnResponse {}
`MsgBurn` provides the ability to destroy nft, thereby guaranteeing the uniqueness of cross-chain nft.
Other business-logic implementation should be defined in other upper-level modules that import this NFT module. The implementation example of the server is as follows:
Other business-logic implementations should be defined in other upper-level modules that import this NFT module. The implementation example of the server is as follows:
```go
type msgServer struct{
@ -126,47 +134,58 @@ type msgServer struct{
}
func (k msgServer) Mint(ctx context.Context, msg *types.MsgMint) (*types.MsgMintResponse, error){
if k.has(msg.id) {
retutn sdkerrors.Wrapf(types.ErrExistedNFT, "%s", msg.id)
nftID := fmt.Sprintf("nft/%s/%s",msg.Denom,nextID())
metadata := bankTypes.Metadata{
Symbol: msg.Metadata.Symbol,
Base: nftID,
Name: msg.Metadata.Name,
URI: msg.Metadata.URI, //TODO
}
_,has := k.bank.GetDenomMetaData(metadata.Base)
if has {
retutn sdkerrors.Wrapf(types.ErrExistedDenom, "%s", msg.Name)
}
nft := types.NFT{msg.Id,msg.Owner,msg.Data}
owner, _ := sdk.AccAddressFromBech32(nft.Owner)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryBare(&nft)
store.Set(types.GetNFTKey(nft.Id), bz)
ownerStore := k.getOwnerStore(ctx, owner)
ownerStore.Set(types.MarshalNFTID(nft.Id), types.MarshalNFTID(nft.Id))
k.bank.SetDenomMetaData(ctx,metadata)
k.bank.MintCoins(types.ModuleName, sdk.Coin{denom: msg.ID, amount: 1})
k.bank.SendCoinsFromModuleToAccount(types.ModuleName, msg.Owner, coin)
nft := NFT{nftID,msg.Metadata.Data}
bz := k.cdc.MustMarshalBinaryBare(&nft)
denomStore := k.getDenomStore(ctx, msg.Denom)
denomStore.Set(nft.Id,bz)
}
func (k msgServer) TransferOwnership(ctx context.Context, msg *types.MsgTransferOwnership) (*types.MsgTransferOwnershipResponse, error){
nft, has := k.GetNFT(ctx, msg.Id)
func (k msgServer) Send(ctx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error){
denom := GetDenomFromID(msg.Id)
denomStore := k.getDenomStore(ctx, denom)
nft, has := denomStore.Get(ctx, msg.Id)
if !has {
return sdkerrors.Wrapf(types.ErrNotFoundNFT, "%s", msg.Id)
}
// remove nft from current owner store
currentOwnerStore := k.getOwnerStore(ctx, nft.Owner)
currentOwnerStore.Delete(types.MarshalNFTID(nft.Id))
k.bank.SendCoins(ctx,msg.Sender,msg.Reveiver,sdk.Coin{denom: msg.ID, amount: 1})
nft.Data = msg.data
nft.Owner = msg.Receiver
k.SetNFT(ctx, nft)
nft.Data := msg.Data
bz := k.cdc.MustMarshalBinaryBare(&nft)
denomStore.Set(nft.Id,bz)
return nil
}
func (k Keeper) Burn(ctx sdk.Context, msg *types.MsgBurn) error {
nft, has := k.GetNFT(ctx, msg.Id)
denom := GetDenomFromID(msg.Id)
denomStore := k.getDenomStore(ctx, denom)
nft, has := denomStore.Get(ctx, msg.Id)
if !has {
return sdkerrors.Wrapf(types.ErrNotFoundNFT, "%s", msg.Id)
}
// delete nft
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetNFTKey(nft.Id))
owner, _ := sdk.AccAddressFromBech32(nft.Owner)
ownerStore := k.getOwnerStore(ctx, owner)
ownerStore.Delete(types.MarshalNFTID(nft.Id))
token := sdk.Coin{denom: msg.ID, amount: 1}
k.bank.SendCoinsFromAccountToModule(ctx,msg.Owner,types.ModuleName,token)
k.bank.BurnCoins(ctx,types.ModuleName,token)
// delete nft
denomStore.Delete(types.GetNFTKey(nft.Id))
return nil
}
@ -175,6 +194,7 @@ func (k Keeper) Burn(ctx sdk.Context, msg *types.MsgBurn) error {
The upper application calls those methods by holding the MsgClient instance of the `x/nft` module. The execution authority of msg is guaranteed by the OCAPs mechanism.
The query service methods for the `x/nft` module are:
```proto
service Query {
@ -220,8 +240,7 @@ No backward incompatibilities.
### Forward Compatibility
This specification conforms to the ERC-721 smart contract specification for NFT identifiers. Note that ERC-721 defines uniqueness based on (contract address, uint256 tokenId), and we conform to this implicitly
because a single module is currently aimed to track NFT identifiers. Note: use of the (mutable) data field to determine uniqueness is not safe.
This specification conforms to the ERC-721 smart contract specification for NFT identifiers. Note that ERC-721 defines uniqueness based on (contract address, uint256 tokenId), and we conform to this implicitly because a single module is currently aimed to track NFT identifiers. Note: use of the (mutable) data field to determine uniqueness is not safe.
### Positive
@ -240,6 +259,7 @@ because a single module is currently aimed to track NFT identifiers. Note: use o
## Further Discussions
For other kinds of applications on the Hub, more app-specific modules can be developed in the future:
- `x/nft/collectibles`: grouping NFTs into collections and defining properties of NFTs such as minting, burning and transferring, etc.
- `x/nft/custody`: custody of NFTs to support trading functionality
- `x/nft/marketplace`: selling and buying NFTs using sdk.Coins