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

Implement ICS-721 NFT transfer #1020

Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ ibc-client-tendermint-types = { version = "0.48.1", path = "./ibc-clients/ics07-
ibc-app-transfer-types = { version = "0.48.1", path = "./ibc-apps/ics20-transfer/types", default-features = false }
ibc-app-nft-transfer-types = { version = "0.48.1", path = "./ibc-apps/ics721-nft-transfer/types", default-features = false }

ibc-proto = { version = "0.39.1", default-features = false }
#ibc-proto = { version = "0.39.1", default-features = false }
ibc-proto = { git = "https://github.com/heliaxdev/ibc-proto-rs", branch = "yuji/feat/ics721-impl", default-features = false }

# cosmos dependencies
tendermint = { version = "0.34.0", default-features = false }
Expand Down
72 changes: 60 additions & 12 deletions ci/no-std-check/Cargo.lock

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

4 changes: 2 additions & 2 deletions ibc-apps/ics20-transfer/types/src/msgs/transfer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Defines the token transfer message type
//! Defines the Non-Fungible Token Transfer message type

use ibc_core::channel::types::error::PacketError;
use ibc_core::channel::types::timeout::TimeoutHeight;
Expand All @@ -15,7 +15,7 @@ use crate::packet::PacketData;

pub(crate) const TYPE_URL: &str = "/ibc.applications.transfer.v1.MsgTransfer";

/// Message used to build an ICS20 token transfer packet.
/// Message used to build an ICS-721 Non-Fungible Token Transfer packet.
///
/// Note that this message is not a packet yet, as it lacks the proper sequence
/// number, and destination port/channel. This is by design. The sender of the
Expand Down
177 changes: 175 additions & 2 deletions ibc-apps/ics721-nft-transfer/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,181 @@
//! Defines the required context traits for ICS-721 to interact with host
//! machine.
use ibc_core::host::types::identifiers::{ChannelId, PortId};
use ibc_core::primitives::prelude::*;
use ibc_core::primitives::Signer;

use crate::types::error::NftTransferError;
use crate::types::{
ClassData, ClassId, ClassUri, Memo, PrefixedClassId, TokenData, TokenId, TokenUri,
};

pub trait NftContext {
/// Get the class ID of the token
fn get_class_id(&self) -> &ClassId;

/// Get the token ID
fn get_id(&self) -> &TokenId;

/// Get the token URI
fn get_uri(&self) -> &TokenUri;

/// Get the token Data
fn get_data(&self) -> &TokenData;
}

pub trait NftClassContext {
/// Get the class ID
fn get_id(&self) -> &ClassId;

/// Get the class URI
fn get_uri(&self) -> &ClassUri;

/// Get the class Data
fn get_data(&self) -> &ClassData;
}

/// Read-only methods required in NFT transfer validation context.
pub trait NftTransferValidationContext {}
pub trait NftTransferValidationContext {
type AccountId: TryFrom<Signer> + PartialEq;
type Nft: NftContext;
type NftClass: NftClassContext;

/// get_port returns the portID for the transfer module.
fn get_port(&self) -> Result<PortId, NftTransferError>;

/// Returns Ok() if the host chain supports sending NFTs.
fn can_send_nft(&self) -> Result<(), NftTransferError>;

/// Returns Ok() if the host chain supports receiving NFTs.
fn can_receive_nft(&self) -> Result<(), NftTransferError>;

/// Validates that the NFT can be created or updated successfully.
fn create_or_update_class_validate(
&self,
class_id: &PrefixedClassId,
class_uri: &ClassUri,
class_data: &ClassData,
) -> Result<(), NftTransferError>;

/// Validates that the tokens can be escrowed successfully.
///
/// The owner of the NFT should be checked in this validation.
/// `memo` field allows to incorporate additional contextual details in the
/// escrow validation.
fn escrow_nft_validate(
&self,
from_account: &Self::AccountId,
port_id: &PortId,
channel_id: &ChannelId,
class_id: &PrefixedClassId,
token_id: &TokenId,
memo: &Memo,
) -> Result<(), NftTransferError>;

/// Validates that the NFT can be unescrowed successfully.
fn unescrow_nft_validate(
&self,
to_account: &Self::AccountId,
port_id: &PortId,
channel_id: &ChannelId,
class_id: &PrefixedClassId,
token_id: &TokenId,
) -> Result<(), NftTransferError>;

/// Validates the receiver account and the NFT input
fn mint_nft_validate(
&self,
account: &Self::AccountId,
class_id: &PrefixedClassId,
token_id: &TokenId,
token_uri: &TokenUri,
token_data: &TokenData,
) -> Result<(), NftTransferError>;

/// Validates the sender account and the coin input before burning.
///
/// The owner of the NFT should be checked in this validation.
/// `memo` field allows to incorporate additional contextual details in the
/// burn validation.
fn burn_nft_validate(
&self,
account: &Self::AccountId,
class_id: &PrefixedClassId,
token_id: &TokenId,
memo: &Memo,
) -> Result<(), NftTransferError>;

/// Returns a hash of the prefixed class ID.
/// Implement only if the host chain supports hashed class ID.
fn class_hash_string(&self, _class_id: &PrefixedClassId) -> Option<String> {
None
}

/// Returns the NFT
fn get_nft(
&self,
class_id: &PrefixedClassId,
token_id: &TokenId,
) -> Result<Self::Nft, NftTransferError>;

/// Returns the NFT class
fn get_nft_class(&self, class_id: &PrefixedClassId)
-> Result<Self::NftClass, NftTransferError>;
}

/// Read-write methods required in NFT transfer execution context.
pub trait NftTransferExecutionContext: NftTransferValidationContext {}
pub trait NftTransferExecutionContext: NftTransferValidationContext {
/// Creates a new NFT Class identified by classId. If the class ID already exists, it updates the class metadata.
fn create_or_update_class_execute(
&self,
class_id: &PrefixedClassId,
class_uri: &ClassUri,
class_data: &ClassData,
) -> Result<(), NftTransferError>;

/// Executes the escrow of the NFT in a user account.
///
/// `memo` field allows to incorporate additional contextual details in the
/// escrow execution.
fn escrow_nft_execute(
&mut self,
from_account: &Self::AccountId,
port_id: &PortId,
channel_id: &ChannelId,
class_id: &PrefixedClassId,
token_id: &TokenId,
memo: &Memo,
) -> Result<(), NftTransferError>;

/// Executes the unescrow of the NFT in a user account.
fn unescrow_nft_execute(
&mut self,
to_account: &Self::AccountId,
port_id: &PortId,
channel_id: &ChannelId,
class_id: &PrefixedClassId,
token_id: &TokenId,
) -> Result<(), NftTransferError>;

/// Executes minting of the NFT in a user account.
fn mint_nft_execute(
&mut self,
account: &Self::AccountId,
class_id: &PrefixedClassId,
token_id: &TokenId,
token_uri: &TokenUri,
token_data: &TokenData,
) -> Result<(), NftTransferError>;

/// Executes burning of the NFT in a user account.
///
/// `memo` field allows to incorporate additional contextual details in the
/// burn execution.
fn burn_nft_execute(
&mut self,
account: &Self::AccountId,
class_id: &PrefixedClassId,
token_id: &TokenId,
memo: &Memo,
) -> Result<(), NftTransferError>;
}
Loading
Loading