Skip to content

Commit

Permalink
Problem: Missing ADR draft for NFT transition to the SDK module (#674) (
Browse files Browse the repository at this point in the history
fixes #673)

Solution: Added a draft ADR for transition

* Added backwards compatibility details and deprecation plan

* Add duplicate messages point in negatives
  • Loading branch information
devashishdxt authored Nov 10, 2021
1 parent dc26c7b commit 0f99b25
Showing 1 changed file with 273 additions and 0 deletions.
273 changes: 273 additions & 0 deletions doc/architecture/adr-004.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
# Transition to Cosmos SDK's NFT module

## Changelog

- 04-11-2021: Initial Draft

## Context

Currently, Crypto.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](https://github.com/irisnet/irismod/tree/master/modules/nft).
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.

## Decision

There are multiple ways to address above mentioned issues.

1. Use Cosmos SDK's NFT module

To solve interportablity and standardisation issues, we can adopt [Cosmos SDK's NFT module](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-043-nft-module.md)
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](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-043-nft-module.md#interoperability)
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](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-041-in-place-store-migrations.md)).

**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.

2. Modify current NFT implementation in `chain-main`

Cosmos SDK's NFT module [ADR](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-043-nft-module.md)
outlines the API contract implemented by NFT module.

**Advantages**

- It'll be very easy to add custom logic base on our requirements (for example, https://github.com/irisnet/irismod/issues/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

Cosmos SDK's NFT module provides only one message type that can be used to send NFTs to someone.

```protobuf
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.

```protobuf
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 the `NewClass` of the `x/nft` module.
- `MsgUpdateClass` - Receive the user's request to update a class, and call the `UpdateClass` of the `x/nft` module.
- `MsgMintNFT` - Receive the user's request to mint a nft, and call the `MintNFT` of the `x/nft` module.
- `BurnNFT` - Receive the user's request to burn a nft, and call the `BurnNFT` of the `x/nft` module.
- `UpdateNFT` - Receive the user's request to update a nft, and call the `UpdateNFT` of the `x/nft` module.

### Backwards Compatibility

#### `MsgIssueDenom`

```protobuf
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 to `id` field of `Class`
- `name` can be mapped to `name` field of `Class`
- `schema` can be mapped to `data` field of `Class`
- There is no field for storing `sender` information in `Class` so, `sender` information must be injected in `data`
field of `Class`

Once the `Class` object is prepared, we can call `NewClass` of `x/nft` module.

#### `MsgMintNFT`

```protobuf
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 to `id` field of `NFT`
- `denom_id` can be mapped to `class_id` field of `NFT`
- `uri` can be mapped to `uri` field of `NFT`
- Since there is no field for `name` in `NFT`, we'll have to inject name in `data` field of `NFT`
- Owners in Cosmos SDK's NFT module are tracked differently. So, we do not have to set `owner` (it does not exist) field
in `NFT`
- `data` can be mapped to `data` field in `NFT`

Once the `NFT` object is prepared, we can call `MintNFT` of `x/nft` module.

#### `MsgEditNFT`

```protobuf
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 to `id` field of `NFT`
- `denom_id` can be mapped to `class_id` field of `NFT`
- `uri` can be mapped to `uri` field of `NFT`
- Since there is no field for `name` in `NFT`, we'll have to inject name in `data` field of `NFT`
- `data` can be mapped to `data` field in `NFT`

Once the `NFT` object is prepared, we can call the `UpdateNFT` of `x/nft` module.

#### `MsgTransferNFT`

```protobuf
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 to `id` of `MsgSend`
- `denom_id` can be mapped to `class_id` of `MsgSend`
- `sender` can be mapped to `sender` of `MsgSend`
- `recipient` can be mapped to `receiver` of `MsgSend`

Once `MsgSend` object is created, we can call the `Send` of `x/nft` module.

#### `MsgBurnNFT`

```protobuf
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).

### Deprecation Plan

On the release of new wrapper NFT module on Crypto.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.

## Status

Draft

## Consequences

### Positive

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](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-033-protobuf-inter-module-comm.md))

### Negative

- 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](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-043-nft-module.md#abstract). 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.

### Neutral

Our current implementation is very similar to Cosmos SDK's NFT module implementation and all the usecases currently
supported by Crypto.org Chain can easily be supported using new module.

## References

- [Cosmos SDK's NFT Module ADR](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-043-nft-module.md)
- [Current NFT Module specs](https://github.com/crypto-org-chain/chain-main/tree/master/x/nft/spec)

0 comments on commit 0f99b25

Please sign in to comment.