From c609f7e0474768026792c3b4412827e2e5d331ad Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Wed, 20 Dec 2023 09:45:24 -0800 Subject: [PATCH] feat: establish ICS-721 boilerplate, ready for new additions (#1012) * chore: establish ics721 boilerplate, ready for new additions * nit --- Cargo.toml | 4 + ibc-apps/Cargo.toml | 8 +- ibc-apps/README.md | 5 + ibc-apps/ics721-nft-transfer/Cargo.toml | 53 ++++++ ibc-apps/ics721-nft-transfer/src/context.rs | 8 + .../ics721-nft-transfer/src/handler/mod.rs | 2 + ibc-apps/ics721-nft-transfer/src/lib.rs | 30 ++++ ibc-apps/ics721-nft-transfer/src/module.rs | 167 ++++++++++++++++++ ibc-apps/ics721-nft-transfer/types/Cargo.toml | 71 ++++++++ .../ics721-nft-transfer/types/src/error.rs | 50 ++++++ .../ics721-nft-transfer/types/src/events.rs | 1 + ibc-apps/ics721-nft-transfer/types/src/lib.rs | 22 +++ .../ics721-nft-transfer/types/src/msgs/mod.rs | 1 + ibc-apps/src/lib.rs | 8 + 14 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 ibc-apps/ics721-nft-transfer/Cargo.toml create mode 100644 ibc-apps/ics721-nft-transfer/src/context.rs create mode 100644 ibc-apps/ics721-nft-transfer/src/handler/mod.rs create mode 100644 ibc-apps/ics721-nft-transfer/src/lib.rs create mode 100644 ibc-apps/ics721-nft-transfer/src/module.rs create mode 100644 ibc-apps/ics721-nft-transfer/types/Cargo.toml create mode 100644 ibc-apps/ics721-nft-transfer/types/src/error.rs create mode 100644 ibc-apps/ics721-nft-transfer/types/src/events.rs create mode 100644 ibc-apps/ics721-nft-transfer/types/src/lib.rs create mode 100644 ibc-apps/ics721-nft-transfer/types/src/msgs/mod.rs diff --git a/Cargo.toml b/Cargo.toml index bda0b4595..014f149ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ members = [ "ibc-clients", "ibc-apps/ics20-transfer/types", "ibc-apps/ics20-transfer", + "ibc-apps/ics721-nft-transfer/types", + "ibc-apps/ics721-nft-transfer", "ibc-apps", "ibc-core/ics24-host/cosmos", "ibc-data-types", @@ -73,6 +75,7 @@ ibc-core-handler = { version = "0.48.1", path = "./ibc-core/ics25-handler", ibc-core-router = { version = "0.48.1", path = "./ibc-core/ics26-routing", default-features = false } ibc-client-tendermint = { version = "0.48.1", path = "./ibc-clients/ics07-tendermint", default-features = false } ibc-app-transfer = { version = "0.48.1", path = "./ibc-apps/ics20-transfer", default-features = false } +ibc-app-nft-transfer = { version = "0.48.1", path = "./ibc-apps/ics721-nft-transfer", default-features = false } ibc-core-client-context = { version = "0.48.1", path = "./ibc-core/ics02-client/context", default-features = false } ibc-core-client-types = { version = "0.48.1", path = "./ibc-core/ics02-client/types", default-features = false } @@ -85,6 +88,7 @@ ibc-core-handler-types = { version = "0.48.1", path = "./ibc-core/ics25-han ibc-core-router-types = { version = "0.48.1", path = "./ibc-core/ics26-routing/types", default-features = false } ibc-client-tendermint-types = { version = "0.48.1", path = "./ibc-clients/ics07-tendermint/types", default-features = false } 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 } diff --git a/ibc-apps/Cargo.toml b/ibc-apps/Cargo.toml index 9057c5e7f..eb5811982 100644 --- a/ibc-apps/Cargo.toml +++ b/ibc-apps/Cargo.toml @@ -17,24 +17,30 @@ description = """ all-features = true [dependencies] -ibc-app-transfer = { workspace = true } +ibc-app-transfer = { workspace = true } +ibc-app-nft-transfer = { workspace = true } [features] default = ["std"] std = [ "ibc-app-transfer/std", + "ibc-app-nft-transfer/std", ] serde = [ "ibc-app-transfer/serde", + "ibc-app-nft-transfer/serde", ] schema = [ "ibc-app-transfer/schema", + "ibc-app-nft-transfer/schema", "serde", "std", ] borsh = [ "ibc-app-transfer/borsh", + "ibc-app-nft-transfer/borsh", ] parity-scale-codec = [ "ibc-app-transfer/parity-scale-codec", + "ibc-app-nft-transfer/parity-scale-codec", ] diff --git a/ibc-apps/README.md b/ibc-apps/README.md index 71c8848df..103e68bc2 100644 --- a/ibc-apps/README.md +++ b/ibc-apps/README.md @@ -26,6 +26,11 @@ applications: - [ibc-app-transfer](./../ibc-apps/ics20-transfer) - [ibc-app-transfer-types](./../ibc-apps/ics20-transfer/types) +### ICS-721: Non-Fungible Token Transfer Application + +- [ibc-app-nft-transfer](./../ibc-apps/ics721-nft-transfer) +- [ibc-app-nft-transfer-types](./../ibc-apps/ics721-nft-transfer/types) + ## Contributing IBC is specified in English in the [cosmos/ibc diff --git a/ibc-apps/ics721-nft-transfer/Cargo.toml b/ibc-apps/ics721-nft-transfer/Cargo.toml new file mode 100644 index 000000000..02cad3270 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "ibc-app-nft-transfer" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = ["cosmos", "ibc", "nft", "transfer", "ics721"] +readme = "./../README.md" +description = """ + Maintained by `ibc-rs`, contains the implementation of the ICS-721 Non-Fungible Token Transfer + application logic and re-exports essential data structures and domain types from + `ibc-app-nft-transfer-types` crate. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +serde_json = { workspace = true, optional = true } + +# ibc dependencies +ibc-app-nft-transfer-types = { workspace = true } +ibc-core = { workspace = true } + +[features] +default = ["std"] +std = [ + "ibc-app-nft-transfer-types/std", + "ibc-core/std", + "serde_json/std", +] +serde = [ + "ibc-app-nft-transfer-types/serde", + "ibc-core/serde", + "serde_json" +] +schema = [ + "ibc-app-nft-transfer-types/schema", + "ibc-core/schema", + "serde", + "std", +] +borsh = [ + "ibc-app-nft-transfer-types/borsh", + "ibc-core/borsh", +] +parity-scale-codec = [ + "ibc-app-nft-transfer-types/parity-scale-codec", + "ibc-core/parity-scale-codec", +] \ No newline at end of file diff --git a/ibc-apps/ics721-nft-transfer/src/context.rs b/ibc-apps/ics721-nft-transfer/src/context.rs new file mode 100644 index 000000000..fe7967a17 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/src/context.rs @@ -0,0 +1,8 @@ +//! Defines the required context traits for ICS-721 to interact with host +//! machine. + +/// Read-only methods required in NFT transfer validation context. +pub trait NftTransferValidationContext {} + +/// Read-write methods required in NFT transfer execution context. +pub trait NftTransferExecutionContext: NftTransferValidationContext {} diff --git a/ibc-apps/ics721-nft-transfer/src/handler/mod.rs b/ibc-apps/ics721-nft-transfer/src/handler/mod.rs new file mode 100644 index 000000000..d56606e9d --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/src/handler/mod.rs @@ -0,0 +1,2 @@ +//! Implements IBC handlers responsible for processing Non-Fungible Token +//! Transfers (ICS-721) messages. diff --git a/ibc-apps/ics721-nft-transfer/src/lib.rs b/ibc-apps/ics721-nft-transfer/src/lib.rs new file mode 100644 index 000000000..fe73af5a9 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/src/lib.rs @@ -0,0 +1,30 @@ +//! Implementation of the IBC [Non-Fungible Token +//! Transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-721-nft-transfer/README.md) +//! (ICS-721) application logic. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +pub mod context; +pub mod handler; +pub mod module; + +/// Re-exports the implementation of the IBC [Non-Fungible Token +/// Transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md) +/// (ICS-721) data structures. +pub mod types { + #[doc(inline)] + pub use ibc_app_nft_transfer_types::*; +} diff --git a/ibc-apps/ics721-nft-transfer/src/module.rs b/ibc-apps/ics721-nft-transfer/src/module.rs new file mode 100644 index 000000000..7e83bf870 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/src/module.rs @@ -0,0 +1,167 @@ +//! Provides IBC module callbacks implementation for the ICS-721 transfer. + +use ibc_app_nft_transfer_types::error::NftTransferError; +use ibc_core::channel::types::acknowledgement::Acknowledgement; +use ibc_core::channel::types::channel::{Counterparty, Order}; +use ibc_core::channel::types::packet::Packet; +use ibc_core::channel::types::Version; +use ibc_core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc_core::primitives::Signer; +use ibc_core::router::types::module::ModuleExtras; + +use crate::context::{NftTransferExecutionContext, NftTransferValidationContext}; + +pub fn on_chan_open_init_validate( + _ctx: &impl NftTransferValidationContext, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + _version: &Version, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_init_execute( + _ctx: &mut impl NftTransferExecutionContext, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + _version: &Version, +) -> Result<(ModuleExtras, Version), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_try_validate( + _ctx: &impl NftTransferValidationContext, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + _counterparty_version: &Version, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_try_execute( + _ctx: &mut impl NftTransferExecutionContext, + _order: Order, + _connection_hops: &[ConnectionId], + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty: &Counterparty, + _counterparty_version: &Version, +) -> Result<(ModuleExtras, Version), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_ack_validate( + _ctx: &impl NftTransferExecutionContext, + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty_version: &Version, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_ack_execute( + _ctx: &mut impl NftTransferExecutionContext, + _port_id: &PortId, + _channel_id: &ChannelId, + _counterparty_version: &Version, +) -> Result { + unimplemented!() +} + +pub fn on_chan_open_confirm_validate( + _ctx: &impl NftTransferValidationContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_open_confirm_execute( + _ctx: &mut impl NftTransferExecutionContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result { + unimplemented!() +} + +pub fn on_chan_close_init_validate( + _ctx: &impl NftTransferValidationContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_close_init_execute( + _ctx: &mut impl NftTransferExecutionContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result { + unimplemented!() +} + +pub fn on_chan_close_confirm_validate( + _ctx: &impl NftTransferValidationContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_chan_close_confirm_execute( + _ctx: &mut impl NftTransferExecutionContext, + _port_id: &PortId, + _channel_id: &ChannelId, +) -> Result { + unimplemented!() +} + +pub fn on_recv_packet_execute( + _ctx_b: &mut impl NftTransferExecutionContext, + _packet: &Packet, +) -> (ModuleExtras, Acknowledgement) { + unimplemented!() +} + +pub fn on_acknowledgement_packet_validate( + _ctx: &impl NftTransferValidationContext, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_acknowledgement_packet_execute( + _ctx: &mut impl NftTransferExecutionContext, + _packet: &Packet, + _acknowledgement: &Acknowledgement, + _relayer: &Signer, +) -> (ModuleExtras, Result<(), NftTransferError>) { + unimplemented!() +} + +pub fn on_timeout_packet_validate( + _ctx: &impl NftTransferValidationContext, + _packet: &Packet, + _relayer: &Signer, +) -> Result<(), NftTransferError> { + unimplemented!() +} + +pub fn on_timeout_packet_execute( + _ctx: &mut impl NftTransferExecutionContext, + _packet: &Packet, + _relayer: &Signer, +) -> (ModuleExtras, Result<(), NftTransferError>) { + unimplemented!() +} diff --git a/ibc-apps/ics721-nft-transfer/types/Cargo.toml b/ibc-apps/ics721-nft-transfer/types/Cargo.toml new file mode 100644 index 000000000..ca9467185 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/types/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "ibc-app-nft-transfer-types" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +keywords = ["cosmos", "ibc", "transfer", "nft", "ics721"] +readme = "./../../README.md" +description = """ + Maintained by `ibc-rs`, encapsulates essential ICS-721 Non-Fungible Token Transfer data structures and + domain types, as specified in the Inter-Blockchain Communication (IBC) protocol. Designed for universal + applicability to facilitate development and integration across diverse IBC-enabled projects. +""" + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +# external dependencies +borsh = { workspace = true, optional = true } +derive_more = { workspace = true } +displaydoc = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } + +# ibc dependencies +ibc-core = { workspace = true } +ibc-proto = { workspace = true } + +## parity dependencies +parity-scale-codec = { workspace = true , optional = true } +scale-info = { workspace = true , optional = true } + +[dev-dependencies] +serde_json = { workspace = true } +rstest = { workspace = true } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_json/std", + "displaydoc/std", + "ibc-core/std", + "ibc-proto/std", +] +serde = [ + "dep:serde", + "ibc-core/serde", + "ibc-proto/serde", +] +schema = [ + "dep:schemars", + "ibc-core/schema", + "ibc-proto/json-schema", + "serde", + "std" +] +borsh = [ + "dep:borsh", + "ibc-core/borsh", + "ibc-proto/borsh" +] +parity-scale-codec = [ + "dep:parity-scale-codec", + "dep:scale-info", + "ibc-core/parity-scale-codec", + "ibc-proto/parity-scale-codec" +] diff --git a/ibc-apps/ics721-nft-transfer/types/src/error.rs b/ibc-apps/ics721-nft-transfer/types/src/error.rs new file mode 100644 index 000000000..65b857dcf --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/types/src/error.rs @@ -0,0 +1,50 @@ +//! Defines the Non-Fungible Token Transfer (ICS-721) error types. +use core::convert::Infallible; + +use displaydoc::Display; +use ibc_core::channel::types::acknowledgement::StatusValue; +use ibc_core::handler::types::error::ContextError; +use ibc_core::host::types::error::IdentifierError; +use ibc_core::primitives::prelude::*; + +#[derive(Display, Debug)] +pub enum NftTransferError { + /// context error: `{0}` + ContextError(ContextError), + /// invalid identifier: `{0}` + InvalidIdentifier(IdentifierError), +} + +#[cfg(feature = "std")] +impl std::error::Error for NftTransferError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &self { + Self::ContextError(e) => Some(e), + Self::InvalidIdentifier(e) => Some(e), + } + } +} + +impl From for NftTransferError { + fn from(e: Infallible) -> Self { + match e {} + } +} + +impl From for NftTransferError { + fn from(err: ContextError) -> NftTransferError { + Self::ContextError(err) + } +} + +impl From for NftTransferError { + fn from(err: IdentifierError) -> NftTransferError { + Self::InvalidIdentifier(err) + } +} + +impl From for StatusValue { + fn from(err: NftTransferError) -> Self { + StatusValue::new(err.to_string()).expect("error message must not be empty") + } +} diff --git a/ibc-apps/ics721-nft-transfer/types/src/events.rs b/ibc-apps/ics721-nft-transfer/types/src/events.rs new file mode 100644 index 000000000..8130bb580 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/types/src/events.rs @@ -0,0 +1 @@ +//! Defines Non-Fungible Token Transfer (ICS-721) event types. diff --git a/ibc-apps/ics721-nft-transfer/types/src/lib.rs b/ibc-apps/ics721-nft-transfer/types/src/lib.rs new file mode 100644 index 000000000..a77f26c1b --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/types/src/lib.rs @@ -0,0 +1,22 @@ +//! Implementation of the IBC [Non-Fungible Token +//! Transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-721-nft-transfer/README.md) +//! (ICS-721) data structures. +#![no_std] +#![forbid(unsafe_code)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::disallowed_methods, clippy::disallowed_types))] +#![deny( + warnings, + trivial_casts, + trivial_numeric_casts, + unused_import_braces, + unused_qualifications, + rust_2018_idioms +)] + +#[cfg(any(test, feature = "std"))] +extern crate std; + +pub mod error; +pub mod events; +pub mod msgs; diff --git a/ibc-apps/ics721-nft-transfer/types/src/msgs/mod.rs b/ibc-apps/ics721-nft-transfer/types/src/msgs/mod.rs new file mode 100644 index 000000000..0c735cd44 --- /dev/null +++ b/ibc-apps/ics721-nft-transfer/types/src/msgs/mod.rs @@ -0,0 +1 @@ +//! Defines the Non-Fungible Token Transfer (ICS-721) message types. diff --git a/ibc-apps/src/lib.rs b/ibc-apps/src/lib.rs index 246713011..e33de2971 100644 --- a/ibc-apps/src/lib.rs +++ b/ibc-apps/src/lib.rs @@ -18,3 +18,11 @@ pub mod transfer { #[doc(inline)] pub use ibc_app_transfer::*; } + +/// Re-exports the implementation of the IBC [Non-Fungible Token +/// Transfer](https://github.com/cosmos/ibc/blob/main/spec/app/ics-721-nft-transfer/README.md) +/// (ICS-721) application logic. +pub mod nft_transfer { + #[doc(inline)] + pub use ibc_app_nft_transfer::*; +}