- 04-11-2021: Initial Draft
- 09-03-2022: Clarification on backwards incompatibility
Currently, Cronos.org Chain offers support for non-fungible tokens (NFTs) using a custom NFT module in chain-main
. The
current implementation of NFT module is based on Irismod's NFT module.
Given that different chains are using their custom implementation of NFT module, it is very difficult to achieve
flawless interportability of NFTs across multiple chains using IBC. At the same time, there are more usecases that need
to be supported using NFTs which may require IBC support. So, there is a need of a standardized NFT implementation
which has support for reusing NFTs between modules and chains.
There are multiple ways to address above mentioned issues.
-
Use Cosmos SDK's NFT module
To solve interportablity and standardisation issues, we can adopt Cosmos SDK's NFT module which is expected to become standard for implementing NFTs on Cosmos SDK chains and also expected to get support for transferring NFTs via IBC.
More details on interportability of Cosmos SDK's NFT module here
To migrate all the NFTs in current NFT module to new Cosmos SDK NFT module, we can use in-place store migration (outlined in ADR 41).
Advantages
- Because this module is a part of Cosmos SDK, it'll get all the future improvement for free.
- Much easier upgrades to newer versions of NFT module as the upgrades will be tested extensively by the community.
Drawbacks
- It'll become a bit difficult to add custom logic (especially when transferring NFTs) based on our requirements.
-
Modify current NFT implementation in
chain-main
Cosmos SDK's NFT module ADR outlines the API contract implemented by NFT module.
Advantages
- It'll be very easy to add custom logic base on our requirements (for example, irisnet/irismod#183).
Drawbacks
- Because this module will be developed in isolation, we'll have to work extra hard to make sure that it confirms to Cosmos SDK's NFT standards.
- Custom logic added to the module may make it impossible to stay in sync with Cosmos SDK's standards.
This ADR proposes the adoption of Option 1 to use native Cosmos SDK's NFT module.
Cosmos SDK's NFT module provides only one message type that can be used to send NFTs to someone.
service Msg {
rpc Send(MsgSend) returns (MsgSendResponse);
}
message MsgSend {
string class_id = 1;
string id = 2;
string sender = 3;
string reveiver = 4;
}
message MsgSendResponse {}
This is similar to TransferNFT
in current implementation.
service Msg {
rpc TransferNFT(MsgTransferNFT) returns (MsgTransferNFTResponse);
}
message MsgTransferNFT {
string id = 1;
string denom_id = 2;
string sender = 3;
string recipient = 4;
}
message MsgTransferNFTResponse {}
denom_id
in current implementation can directly be mapped to class_id
and receipient
can be mapped to receiver
of Cosmos SDK's NFT module request.
Besides this, Cosmos SDK's NFT module specifies multiple message types that should be implemented by wrapper module.
MsgNewClass
- Receive the user's request to create a class, and call theNewClass
of thex/nft
module.MsgUpdateClass
- Receive the user's request to update a class, and call theUpdateClass
of thex/nft
module.MsgMintNFT
- Receive the user's request to mint a nft, and call theMintNFT
of thex/nft
module.BurnNFT
- Receive the user's request to burn a nft, and call theBurnNFT
of thex/nft
module.UpdateNFT
- Receive the user's request to update a nft, and call theUpdateNFT
of thex/nft
module.
service Msg {
rpc IssueDenom(MsgIssueDenom) returns (MsgIssueDenomResponse);
}
message MsgIssueDenom {
string id = 1;
string name = 2;
string schema = 3;
string sender = 4;
}
message MsgIssueDenomResponse {}
To implement MsgIssueDenom
:
id
can be mapped toid
field ofClass
name
can be mapped toname
field ofClass
schema
can be mapped todata
field ofClass
- There is no field for storing
sender
information inClass
so,sender
information must be injected indata
field ofClass
Once the Class
object is prepared, we can call NewClass
of x/nft
module.
service Msg {
rpc MintNFT(MsgMintNFT) returns (MsgMintNFTResponse);
}
message MsgMintNFT {
string id = 1;
string denom_id = 2;
string name = 3;
string uri = 4;
string data = 5;
string sender = 6;
string recipient = 7;
}
message MsgMintNFTResponse {}
To implement MsgMintNFT
:
id
can be mapped toid
field ofNFT
denom_id
can be mapped toclass_id
field ofNFT
uri
can be mapped touri
field ofNFT
- Since there is no field for
name
inNFT
, we'll have to inject name indata
field ofNFT
- Owners in Cosmos SDK's NFT module are tracked differently. So, we do not have to set
owner
(it does not exist) field inNFT
data
can be mapped todata
field inNFT
Once the NFT
object is prepared, we can call MintNFT
of x/nft
module.
service Msg {
rpc EditNFT(MsgEditNFT) returns (MsgEditNFTResponse);
}
message MsgEditNFT {
string id = 1;
string denom_id = 2;
string name = 3;
string uri = 4;
string data = 5;
string sender = 6;
}
message MsgEditNFTResponse {}
To implement MsgEditNFT
:
id
can be mapped toid
field ofNFT
denom_id
can be mapped toclass_id
field ofNFT
uri
can be mapped touri
field ofNFT
- Since there is no field for
name
inNFT
, we'll have to inject name indata
field ofNFT
data
can be mapped todata
field inNFT
Once the NFT
object is prepared, we can call the UpdateNFT
of x/nft
module.
service Msg {
rpc TransferNFT(MsgTransferNFT) returns (MsgTransferNFTResponse);
}
message MsgTransferNFT {
string id = 1;
string denom_id = 2;
string sender = 3;
string recipient = 4;
}
message MsgTransferNFTResponse {}
Message to transfer the ownership of NFT is directly provided by Cosmos SDK's NFT module using MsgSend
. To implement
MsgTransferNFT
:
id
can be mapped toid
ofMsgSend
denom_id
can be mapped toclass_id
ofMsgSend
sender
can be mapped tosender
ofMsgSend
recipient
can be mapped toreceiver
ofMsgSend
Once MsgSend
object is created, we can call the Send
of x/nft
module.
service Msg {
rpc BurnNFT(MsgBurnNFT) returns (MsgBurnNFTResponse);
}
message MsgBurnNFT {
string id = 1;
string denom_id = 2;
string sender = 3;
}
message MsgBurnNFTResponse {}
To implement MsgBurnNFT
, we can directly call the BurnNFT
of x/nft
(after performing validations).
It is possible to support all the transactions and queries in a backwards compatible way except for DenomByName
query.
Because current implementation of NFT module specifies denom_name
as a unique parameter unlike Cosmos SDK's native
x/nft
module, it is not possible to support a query which returns single Denom
object with given denom_name
.
On the release of new wrapper NFT module on Cronos.org chain, all the above messages should be considered as deprecated and will be removed in approx. 3 months via an upgrade. After the upgrade, the messages specified by Cosmos SDK's NFT module will handle all the operations.
Proposed
Cosmos SDK's module will have support from multiple members of the community and will get all the future improvements for free (for example, IBC support, inter-module asset support using ADR-33)
- Our current implementation is slightly different from Cosmos SDK's NFT module which can result in backwards incompatibility. This can be temporarily avoided by creating a wrapper module (which we anyway need to create for minting and burning NFTs) and adding same messages as the current module along with the sandard message types mentioned here. But, it is necessary to have a deprecation plan for old message types and transition all the downstream services to new standardized message types.
- There'll be a lot of duplicate message types until legacy messages are removed.
Our current implementation is very similar to Cosmos SDK's NFT module implementation and all the usecases currently supported by Cronos.org Chain can easily be supported using new module.