From 3e9a7d04d0652cc2d33074d5ad9393976fa9e115 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Wed, 6 Mar 2024 13:50:32 +0000 Subject: [PATCH 01/10] fix: restore missing ink contract attribute --- contracts/pop-api-examples/nfts/lib.rs | 90 +++++++++++++------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/contracts/pop-api-examples/nfts/lib.rs b/contracts/pop-api-examples/nfts/lib.rs index 77c5820a..28898062 100755 --- a/contracts/pop-api-examples/nfts/lib.rs +++ b/contracts/pop-api-examples/nfts/lib.rs @@ -5,63 +5,63 @@ use pop_api::nfts; #[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum ContractError { - NftsError(nfts::Error), + NftsError(nfts::Error), } impl From for ContractError { - fn from(value: nfts::Error) -> Self { - ContractError::NftsError(value) - } + fn from(value: nfts::Error) -> Self { + ContractError::NftsError(value) + } } #[ink::contract(env = pop_api::Environment)] mod pop_api_extension_demo { - use super::ContractError; + use super::ContractError; - #[ink(storage)] - #[derive(Default)] - pub struct PopApiExtensionDemo; + #[ink(storage)] + #[derive(Default)] + pub struct PopApiExtensionDemo; - impl PopApiExtensionDemo { - #[ink(constructor, payable)] - pub fn new() -> Self { - ink::env::debug_println!("PopApiExtensionDemo::new"); - Default::default() - } + impl PopApiExtensionDemo { + #[ink(constructor, payable)] + pub fn new() -> Self { + ink::env::debug_println!("PopApiExtensionDemo::new"); + Default::default() + } - #[ink(message)] - pub fn mint_through_runtime( - &mut self, - collection_id: u32, - item_id: u32, - receiver: AccountId, - ) -> Result<(), ContractError> { - ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime: collection_id: {:?} \nitem_id {:?} \nreceiver: {:?}, ", collection_id, item_id, receiver); + #[ink(message)] + pub fn mint_through_runtime( + &mut self, + collection_id: u32, + item_id: u32, + receiver: AccountId, + ) -> Result<(), ContractError> { + ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime: collection_id: {:?} \nitem_id {:?} \nreceiver: {:?}, ", collection_id, item_id, receiver); - // simplified API call - let result = pop_api::nfts::mint(collection_id, item_id, receiver); - ink::env::debug_println!( - "PopApiExtensionDemo::mint_through_runtime result: {result:?}" - ); - if let Err(pop_api::nfts::Error::NoConfig) = result { - ink::env::debug_println!( - "PopApiExtensionDemo::mint_through_runtime expected error received" - ); - } - result?; + // simplified API call + let result = pop_api::nfts::mint(collection_id, item_id, receiver); + ink::env::debug_println!( + "PopApiExtensionDemo::mint_through_runtime result: {result:?}" + ); + if let Err(pop_api::nfts::Error::NoConfig) = result { + ink::env::debug_println!( + "PopApiExtensionDemo::mint_through_runtime expected error received" + ); + } + result?; - ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime end"); - Ok(()) - } - } + ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime end"); + Ok(()) + } + } - #[cfg(test)] - mod tests { - use super::*; + #[cfg(test)] + mod tests { + use super::*; - #[ink::test] - fn default_works() { - PopApiExtensionDemo::new(); - } - } + #[ink::test] + fn default_works() { + PopApiExtensionDemo::new(); + } + } } From 4458b9010fcc2924c6444debbe4d2d43dc0617a9 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Wed, 6 Mar 2024 14:36:43 +0000 Subject: [PATCH 02/10] feat(pop-api): further implementation of nfts api --- Cargo.lock | 1 + pop-api/Cargo.toml | 4 +- pop-api/src/lib.rs | 6 +- pop-api/src/v0/nfts.rs | 554 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 500 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bb824fb..50b84e14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9033,6 +9033,7 @@ dependencies = [ name = "pop-api" version = "0.0.0" dependencies = [ + "enumflags2", "ink", "parity-scale-codec", "pop-api-primitives", diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index 51d515c8..69b362f4 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -6,9 +6,10 @@ version = "0.0.0" edition = "2021" [dependencies] +enumflags2 = { version = "0.7.7" } ink = { version = "4.3.0", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } +scale-info = { version = "2.6", default-features = false, features = ["derive"] } sp-io = { version = "23.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } sp-runtime = { version = "24.0", default-features = false } @@ -22,6 +23,7 @@ crate-type = ["rlib"] [features] default = ["std"] std = [ + "enumflags2/std", "ink/std", "pop-api-primitives/std", "scale/std", diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index af96523f..b8b47587 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -17,9 +17,9 @@ pub type CollectionId = u32; // Id used for identifying non-fungible items. pub type ItemId = u32; -type AccountId = ::AccountId; -type Balance = ::Balance; -type BlockNumber = ::BlockNumber; +type AccountId = ::AccountId; +type Balance = ::Balance; +type BlockNumber = ::BlockNumber; type StringLimit = u32; type KeyLimit = u32; type MaxTips = u32; diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs index 90e5cac0..ec75249e 100644 --- a/pop-api/src/v0/nfts.rs +++ b/pop-api/src/v0/nfts.rs @@ -1,10 +1,26 @@ use super::RuntimeCall; use crate::{PopApiError::UnknownStatusCode, *}; use ink::prelude::vec::Vec; -use primitives::{BoundedVec, MultiAddress}; +use primitives::MultiAddress; +use scale::Encode; +use types::*; type Result = core::result::Result; +/// Issue a new collection of non-fungible items +pub fn create( + admin: impl Into>, + config: CollectionConfig, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Create { admin: admin.into(), config }))?) +} + +/// Destroy a collection of fungible items. +pub fn destroy(collection: CollectionId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Destroy { collection }))?) +} + +/// Mint an item of a particular collection. pub fn mint( collection: CollectionId, item: ItemId, @@ -18,14 +34,276 @@ pub fn mint( }))?) } -#[derive(scale::Encode)] -#[allow(dead_code)] +/// Destroy a single item. +pub fn burn(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Burn { collection, item }))?) +} + +/// Move an item from the sender account to another. +pub fn transfer( + collection: CollectionId, + item: ItemId, + dest: impl Into>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Transfer { collection, item, dest: dest.into() }))?) +} + +/// Re-evaluate the deposits on some items. +pub fn redeposit(collection: CollectionId, items: Vec) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Redeposit { collection, items }))?) +} + +/// Disallow further unprivileged transfer of an item. +pub fn lock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemTransfer { collection, item }))?) +} + +/// Re-allow unprivileged transfer of an item. +pub fn unlock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::UnlockItemTransfer { collection, item }))?) +} + +/// Disallows specified settings for the whole collection. +pub fn lock_colelction(collection: CollectionId, lock_settings: CollectionSettings) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockCollection { collection, lock_settings }))?) +} + +/// Change the Owner of a collection. +pub fn transfer_ownership( + collection: CollectionId, + new_owner: impl Into>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::TransferOwnership { + collection, + new_owner: new_owner.into(), + }))?) +} + +/// Change the Issuer, Admin and Freezer of a collection. +pub fn set_team( + collection: CollectionId, + issuer: Option>>, + admin: Option>>, + freezer: Option>>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetTeam { + collection, + issuer: issuer.map(|i| i.into()), + admin: admin.map(|i| i.into()), + freezer: freezer.map(|i| i.into()), + }))?) +} + +/// Approve an item to be transferred by a delegated third-party account. +pub fn approve_transfer( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, + maybe_deadline: Option, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveTransfer { + collection, + item, + delegate: delegate.into(), + maybe_deadline, + }))?) +} + +/// Cancel one of the transfer approvals for a specific item. +pub fn cancel_approval( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelApproval { + collection, + item, + delegate: delegate.into(), + }))?) +} + +/// Cancel all the approvals of a specific item. +pub fn clear_all_transfer_approvals(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAllTransferApprovals { collection, item }))?) +} + +/// Disallows changing the metadata or attributes of the item. +pub fn lock_item_properties( + collection: CollectionId, + item: ItemId, + lock_metadata: bool, + lock_attributes: bool, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemProperties { + collection, + item, + lock_metadata, + lock_attributes, + }))?) +} + +/// Set an attribute for a collection or item. +pub fn set_attribute( + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, + value: BoundedVec, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAttribute { + collection, + maybe_item, + namespace, + key, + value, + }))?) +} + +/// Clear an attribute for a collection or item. +pub fn clear_attribute( + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAttribute { + collection, + maybe_item, + namespace, + key, + }))?) +} + +/// Approve item's attributes to be changed by a delegated third-party account. +pub fn approve_item_attribute( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveItemAttribute { + collection, + item, + delegate: delegate.into(), + }))?) +} + +/// Cancel the previously provided approval to change item's attributes. +pub fn cancel_item_attributes_approval( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelItemAttributesApproval { + collection, + item, + delegate: delegate.into(), + }))?) +} + +/// Set the metadata for an item. +pub fn set_metadata( + collection: CollectionId, + item: ItemId, + data: BoundedVec, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetMetadata { collection, item, data }))?) +} + +/// Clear the metadata for an item. +pub fn clear_metadata(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearMetadata { collection, item }))?) +} + +/// Set the metadata for a collection. +pub fn set_collection_metadata( + collection: CollectionId, + data: BoundedVec, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMetadata { collection, data }))?) +} + +/// Clear the metadata for a collection. +pub fn clear_collection_metadata(collection: CollectionId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearCollectionMetadata { collection }))?) +} + +/// Set (or reset) the acceptance of ownership for a particular account. +pub fn set_accept_ownership( + collection: CollectionId, + maybe_collection: Option, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAcceptOwnership { collection, maybe_collection }))?) +} + +/// Set the maximum number of items a collection could have. +pub fn set_collection_max_supply(collection: CollectionId, max_supply: u32) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMaxSupply { collection, max_supply }))?) +} + +/// Update mint settings. +pub fn update_mint_settings(collection: CollectionId, mint_settings: MintSettings) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::UpdateMintSettings { collection, mint_settings }))?) +} + +/// Set (or reset) the price for an item. +pub fn price(collection: CollectionId, item: ItemId, price: Option) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Price { collection, item, price }))?) +} + +/// Allows to buy an item if it's up for sale. +pub fn buy_item(collection: CollectionId, item: ItemId, bid_price: Balance) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::BuyItem { collection, item, bid_price }))?) +} + +/// Allows to pay the tips. +pub fn pay_tips(tips: BoundedVec) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::PayTips { tips }))?) +} + +/// Register a new atomic swap, declaring an intention to send an `item` in exchange for +/// `desired_item` from origin to target on the current chain. +pub fn create_swap( + offered_collection: CollectionId, + offered_item: ItemId, + desired_collection: CollectionId, + maybe_desired_item: Option, + maybe_price: Option, + duration: BlockNumber, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CreateSwap { + offered_collection, + offered_item, + desired_collection, + maybe_desired_item, + maybe_price, + duration, + }))?) +} + +/// Cancel an atomic swap. +pub fn cancel_swap(offered_collection: CollectionId, offered_item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelSwap { offered_collection, offered_item }))?) +} + +/// Claim an atomic swap. +pub fn claim_swap( + send_collection: CollectionId, + send_item: ItemId, + receive_collection: CollectionId, + receive_item: ItemId, +) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClaimSwap { + send_collection, + send_item, + receive_collection, + receive_item, + }))?) +} + +#[derive(Encode)] pub(crate) enum NftCalls { - // #[codec(index = 0)] - // Create { - // admin: MultiAddress, - // config: CollectionConfig - // }, + #[codec(index = 0)] + Create { admin: MultiAddress, config: CollectionConfig }, #[codec(index = 2)] Destroy { collection: CollectionId }, #[codec(index = 3)] @@ -45,11 +323,8 @@ pub(crate) enum NftCalls { LockItemTransfer { collection: CollectionId, item: ItemId }, #[codec(index = 9)] UnlockItemTransfer { collection: CollectionId, item: ItemId }, - // #[codec(index = 10)] - // LockCollection { - // collection: CollectionId, - // lock_settings: CollectionSettings, - // }, + #[codec(index = 10)] + LockCollection { collection: CollectionId, lock_settings: CollectionSettings }, #[codec(index = 11)] TransferOwnership { collection: CollectionId, new_owner: MultiAddress }, #[codec(index = 12)] @@ -77,21 +352,21 @@ pub(crate) enum NftCalls { lock_metadata: bool, lock_attributes: bool, }, - // #[codec(index = 19)] - // SetAttribute { - // collection: CollectionId, - // maybe_item: Option, - // namespace: AttributeNamespace, - // key: BoundedVec, - // value: BoundedVec, - // }, - // #[codec(index = 21)] - // ClearAttribute { - // collection: CollectionId, - // maybe_item: Option, - // namespace: AttributeNamespace, - // key: BoundedVec, - // }, + #[codec(index = 19)] + SetAttribute { + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, + value: BoundedVec, + }, + #[codec(index = 21)] + ClearAttribute { + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, + }, #[codec(index = 22)] ApproveItemAttribute { collection: CollectionId, @@ -116,28 +391,23 @@ pub(crate) enum NftCalls { SetAcceptOwnership { collection: CollectionId, maybe_collection: Option }, #[codec(index = 29)] SetCollectionMaxSupply { collection: CollectionId, max_supply: u32 }, - // #[codec(index = 30)] - // UpdateMintSettings { - // collection: CollectionId, - // mint_settings: MintSettings, - // }, + #[codec(index = 30)] + UpdateMintSettings { collection: CollectionId, mint_settings: MintSettings }, #[codec(index = 31)] Price { collection: CollectionId, item: ItemId, price: Option }, #[codec(index = 32)] BuyItem { collection: CollectionId, item: ItemId, bid_price: Balance }, - // #[codec(index = 33)] - // PayTips { - // tips: BoundedVec, MaxTips> - // }, - // #[codec(index = 34)] - // CreateSwap { - // offered_collection: CollectionId, - // offered_item: ItemId, - // desired_collection: CollectionId, - // maybe_desired_item: Option, - // maybe_price: Option>, - // duration: BlockNumber, - // }, + #[codec(index = 33)] + PayTips { tips: BoundedVec }, + #[codec(index = 34)] + CreateSwap { + offered_collection: CollectionId, + offered_item: ItemId, + desired_collection: CollectionId, + maybe_desired_item: Option, + maybe_price: Option, + duration: BlockNumber, + }, #[codec(index = 35)] CancelSwap { offered_collection: CollectionId, offered_item: ItemId }, #[codec(index = 36)] @@ -147,21 +417,9 @@ pub(crate) enum NftCalls { receive_collection: CollectionId, receive_item: ItemId, }, - // #[codec(index = 37)] - // MintPreSigned { - // mint_data: PreSignedMint, - // signature: OffchainSignature, - // signer: AccountId - // }, - // #[codec(index = 38)] - // SetAttributesPreSigned { - // data: PreSignedAttributes, - // signature: OffchainSignature, - // signer: AccountId, - // } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum Error { /// The signing account has no permission to do the operation. @@ -320,3 +578,177 @@ impl From for Error { } } } + +// Local implementations of pallet-nfts types +mod types { + use super::*; + use crate::{Balance, BlockNumber, CollectionId}; + use enumflags2::{bitflags, BitFlags}; + use scale::{Decode, EncodeLike, MaxEncodedLen}; + use scale_info::{build::Fields, meta_type, prelude::vec, Path, Type, TypeInfo, TypeParameter}; + + /// Attribute namespaces for non-fungible tokens. + #[derive(Encode)] + pub enum AttributeNamespace { + /// An attribute was set by the pallet. + Pallet, + /// An attribute was set by collection's owner. + CollectionOwner, + /// An attribute was set by item's owner. + ItemOwner, + /// An attribute was set by pre-approved account. + Account(AccountId), + } + + /// Collection's configuration. + #[derive(Encode)] + pub struct CollectionConfig { + /// Collection's settings. + pub settings: CollectionSettings, + /// Collection's max supply. + pub max_supply: Option, + /// Default settings each item will get during the mint. + pub mint_settings: MintSettings, + } + + /// Wrapper type for `BitFlags` that implements `Codec`. + pub struct CollectionSettings(pub BitFlags); + + impl_codec_bitflags!(CollectionSettings, u64, CollectionSetting); + + /// Support for up to 64 user-enabled features on a collection. + #[bitflags] + #[repr(u64)] + #[derive(Copy, Clone, Encode, TypeInfo)] + pub enum CollectionSetting { + /// Items in this collection are transferable. + TransferableItems, + /// The metadata of this collection can be modified. + UnlockedMetadata, + /// Attributes of this collection can be modified. + UnlockedAttributes, + /// The supply of this collection can be modified. + UnlockedMaxSupply, + /// When this isn't set then the deposit is required to hold the items of this collection. + DepositRequired, + } + + /// Support for up to 64 user-enabled features on an item. + #[bitflags] + #[repr(u64)] + #[derive(Copy, Clone, Encode, TypeInfo)] + pub enum ItemSetting { + /// This item is transferable. + Transferable, + /// The metadata of this item can be modified. + UnlockedMetadata, + /// Attributes of this item can be modified. + UnlockedAttributes, + } + + /// Wrapper type for `BitFlags` that implements `Codec`. + pub struct ItemSettings(pub BitFlags); + + impl_codec_bitflags!(ItemSettings, u64, ItemSetting); + + /// Information about the tip. + #[derive(Encode)] + pub struct ItemTip { + /// The collection of the item. + pub(super) collection: CollectionId, + /// An item of which the tip is sent for. + pub(super) item: ItemId, + /// A sender of the tip. + pub(super) receiver: AccountId, + /// An amount the sender is willing to tip. + pub(super) amount: Balance, + } + + /// Holds the information about minting. + #[derive(Encode)] + pub struct MintSettings { + /// Whether anyone can mint or if minters are restricted to some subset. + pub mint_type: MintType, + /// An optional price per mint. + pub price: Option, + /// When the mint starts. + pub start_block: Option, + /// When the mint ends. + pub end_block: Option, + /// Default settings each item will get during the mint. + pub default_item_settings: ItemSettings, + } + + /// Mint type. Can the NFT be create by anyone, or only the creator of the collection, + /// or only by wallets that already hold an NFT from a certain collection? + /// The ownership of a privately minted NFT is still publicly visible. + #[derive(Encode)] + pub enum MintType { + /// Only an `Issuer` could mint items. + Issuer, + /// Anyone could mint items. + Public, + /// Only holders of items in specified collection could mint new items. + HolderOf(CollectionId), + } + + /// Holds the details about the price. + #[derive(Encode)] + pub struct PriceWithDirection { + /// An amount. + pub(super) amount: Balance, + /// A direction (send or receive). + pub(super) direction: PriceDirection, + } + + /// Specifies whether the tokens will be sent or received. + #[derive(Encode)] + pub enum PriceDirection { + /// Tokens will be sent. + Send, + /// Tokens will be received. + Receive, + } + + macro_rules! impl_codec_bitflags { + ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { + impl MaxEncodedLen for $wrapper { + fn max_encoded_len() -> usize { + <$size>::max_encoded_len() + } + } + impl Encode for $wrapper { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } + } + impl EncodeLike for $wrapper {} + impl Decode for $wrapper { + fn decode( + input: &mut I, + ) -> core::result::Result { + let field = <$size>::decode(input)?; + Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) + } + } + + impl TypeInfo for $wrapper { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new( + "T", + Some(meta_type::<$bitflag_enum>()), + )]) + .composite( + Fields::unnamed() + .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), + ) + } + } + }; + } + pub(crate) use impl_codec_bitflags; +} From 37bcd48a4cd771e56b78f1428ffac0a6a724f472 Mon Sep 17 00:00:00 2001 From: Peter White Date: Wed, 6 Mar 2024 23:27:41 -0700 Subject: [PATCH 03/10] feat(pop-api): add support to query nft state in extension --- pop-api/primitives/src/storage_keys.rs | 12 +++ runtime/src/extensions.rs | 122 +++++++++++++++++++++---- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/pop-api/primitives/src/storage_keys.rs b/pop-api/primitives/src/storage_keys.rs index 8b107eec..c90851ab 100644 --- a/pop-api/primitives/src/storage_keys.rs +++ b/pop-api/primitives/src/storage_keys.rs @@ -2,6 +2,7 @@ use scale::{Decode, Encode, MaxEncodedLen}; #[derive(Encode, Decode, Debug, MaxEncodedLen)] pub enum RuntimeStateKeys { + Nfts(NftsKeys), ParachainSystem(ParachainSystemKeys), } @@ -9,3 +10,14 @@ pub enum RuntimeStateKeys { pub enum ParachainSystemKeys { LastRelayChainBlockNumber, } + +// https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/nfts/src/impl_nonfungibles.rs +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +pub enum NftsKeys { + Owner, + CollectionOwner, + Attribute, + CustomAttribute, + SystemAttribute, + CollectionAttribute, +} diff --git a/runtime/src/extensions.rs b/runtime/src/extensions.rs index 4b3eefbb..92663645 100644 --- a/runtime/src/extensions.rs +++ b/runtime/src/extensions.rs @@ -2,12 +2,13 @@ use cumulus_primitives_core::relay_chain::BlockNumber; use frame_support::{ dispatch::{GetDispatchInfo, PostDispatchInfo, RawOrigin}, pallet_prelude::*, + traits::nonfungibles_v2::Inspect, }; use log; use pallet_contracts::chain_extension::{ - ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, + BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, }; -use pop_api_primitives::storage_keys::ParachainSystemKeys; +use pop_api_primitives::storage_keys::{NftsKeys, ParachainSystemKeys, RuntimeStateKeys}; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; @@ -20,7 +21,7 @@ pub struct PopApiExtension; impl ChainExtension for PopApiExtension where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config, + T: pallet_contracts::Config + pallet_nfts::Config + cumulus_pallet_parachain_system::Config, ::AccountId: UncheckedFrom<::Hash> + AsRef<[u8]>, ::RuntimeCall: Parameter + Dispatchable::RuntimeOrigin, PostInfo = PostDispatchInfo> @@ -137,9 +138,96 @@ where } } +fn read_nfts_state( + key: NftsKeys, + env: &mut Environment, +) -> Result, DispatchError> +where + T: pallet_contracts::Config + pallet_nfts::Config + frame_system::Config, + E: Ext, +{ + match key { + NftsKeys::Owner => { + let (collection, item): ( + ::CollectionId, + ::ItemId, + ) = env.read_as()?; + + let maybe_owner = pallet_nfts::Pallet::::owner(collection, item); + Ok(maybe_owner.encode()) + }, + NftsKeys::CollectionOwner => { + let collection: ::CollectionId = env.read_as()?; + + let maybe_owner = pallet_nfts::Pallet::::collection_owner(collection); + + Ok(maybe_owner.encode()) + }, + NftsKeys::Attribute => { + // TODO: charge weight + let len = env.in_len(); + + let (collection, item, key): ( + ::CollectionId, + ::ItemId, + Vec, + ) = env.read_as_unbounded(len)?; + + let maybe_attribute = pallet_nfts::Pallet::::attribute(&collection, &item, &key); + + Ok(maybe_attribute.encode()) + }, + NftsKeys::CustomAttribute => { + // TODO: charge weight + let len = env.in_len(); + + let (account, collection, item, key): ( + ::AccountId, + ::CollectionId, + ::ItemId, + Vec, + ) = env.read_as_unbounded(len)?; + + let maybe_attribute = + pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key); + + Ok(maybe_attribute.encode()) + }, + NftsKeys::SystemAttribute => { + // TODO: charge weight + let len = env.in_len(); + + let (collection, maybe_item, key): ( + ::CollectionId, + Option<::ItemId>, + Vec, + ) = env.read_as_unbounded(len)?; + + let maybe_attribute = + pallet_nfts::Pallet::::system_attribute(&collection, maybe_item.as_ref(), &key); + + Ok(maybe_attribute.encode()) + }, + NftsKeys::CollectionAttribute => { + // TODO: charge weight + let len = env.in_len(); + + let (collection, key): (::CollectionId, Vec) = + env.read_as_unbounded(len)?; + + let maybe_attribute = pallet_nfts::Pallet::::collection_attribute(&collection, &key); + + Ok(maybe_attribute.encode()) + }, + } +} + fn read_state(env: Environment) -> Result<(), DispatchError> where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config + frame_system::Config, + T: pallet_contracts::Config + + pallet_nfts::Config + + cumulus_pallet_parachain_system::Config + + frame_system::Config, E: Ext, { const LOG_PREFIX: &str = " read_state |"; @@ -155,23 +243,25 @@ where log::debug!(target:LOG_TARGET, "{} charged weight: {:?}", LOG_PREFIX, charged_weight); - let key: ParachainSystemKeys = env.read_as()?; + let key: RuntimeStateKeys = env.read_as()?; let result = match key { - ParachainSystemKeys::LastRelayChainBlockNumber => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - let relay_block_num: BlockNumber = - cumulus_pallet_parachain_system::Pallet::::last_relay_block_number(); - log::debug!( - target:LOG_TARGET, - "{} last relay chain block number is: {:?}.", LOG_PREFIX, relay_block_num - ); - relay_block_num + RuntimeStateKeys::Nfts(key) => read_nfts_state::(key, &mut env), + RuntimeStateKeys::ParachainSystem(key) => match key { + ParachainSystemKeys::LastRelayChainBlockNumber => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + let relay_block_num: BlockNumber = + cumulus_pallet_parachain_system::Pallet::::last_relay_block_number(); + log::debug!( + target:LOG_TARGET, + "{} last relay chain block number is: {:?}.", LOG_PREFIX, relay_block_num + ); + Ok(relay_block_num.encode()) + }, }, } - .encode() - // Double-encode result for extension return type of bytes .encode(); + log::trace!( target:LOG_TARGET, "{} result: {:?}.", LOG_PREFIX, result From fa1155e6562da482d025fd2e98b289ce4a4b3d06 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 14:27:27 +0000 Subject: [PATCH 04/10] feat(pop-api): continue support for nft state queries --- Cargo.lock | 1 + contracts/pop-api-examples/nfts/lib.rs | 41 ++++--- pop-api/primitives/Cargo.toml | 2 + pop-api/primitives/src/lib.rs | 15 +++ pop-api/primitives/src/storage_keys.rs | 23 ++-- pop-api/src/lib.rs | 7 -- pop-api/src/v0/nfts.rs | 97 ++++++++++++++++- runtime/src/assets_config.rs | 4 + runtime/src/extensions.rs | 144 ++++++++++--------------- 9 files changed, 219 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50b84e14..b139ee54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9046,6 +9046,7 @@ dependencies = [ name = "pop-api-primitives" version = "0.0.0" dependencies = [ + "bounded-collections 0.1.9", "parity-scale-codec", "scale-info", ] diff --git a/contracts/pop-api-examples/nfts/lib.rs b/contracts/pop-api-examples/nfts/lib.rs index 28898062..02fbf986 100755 --- a/contracts/pop-api-examples/nfts/lib.rs +++ b/contracts/pop-api-examples/nfts/lib.rs @@ -5,7 +5,10 @@ use pop_api::nfts; #[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] pub enum ContractError { + InvalidCollection, + ItemAlreadyExists, NftsError(nfts::Error), + NotOwner, } impl From for ContractError { @@ -25,7 +28,7 @@ mod pop_api_extension_demo { impl PopApiExtensionDemo { #[ink(constructor, payable)] pub fn new() -> Self { - ink::env::debug_println!("PopApiExtensionDemo::new"); + ink::env::debug_println!("Contract::new"); Default::default() } @@ -36,21 +39,35 @@ mod pop_api_extension_demo { item_id: u32, receiver: AccountId, ) -> Result<(), ContractError> { - ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime: collection_id: {:?} \nitem_id {:?} \nreceiver: {:?}, ", collection_id, item_id, receiver); - - // simplified API call - let result = pop_api::nfts::mint(collection_id, item_id, receiver); ink::env::debug_println!( - "PopApiExtensionDemo::mint_through_runtime result: {result:?}" + "Contract::mint_through_runtime: collection_id: {:?} item_id {:?} receiver: {:?}", + collection_id, + item_id, + receiver ); - if let Err(pop_api::nfts::Error::NoConfig) = result { - ink::env::debug_println!( - "PopApiExtensionDemo::mint_through_runtime expected error received" - ); + + // Check if item already exists (demo purposes only, unnecessary as would expect check in mint call) + if pop_api::nfts::item(collection_id, item_id)?.is_some() { + return Err(ContractError::ItemAlreadyExists); + } + + // mint api + pop_api::nfts::mint(collection_id, item_id, receiver)?; + ink::env::debug_println!("Contract::mint_through_runtime: item minted successfully"); + + // check owner + match pop_api::nfts::owner(collection_id, item_id)? { + Some(owner) if owner == receiver => { + ink::env::debug_println!( + "Contract::mint_through_runtime success: minted item belongs to receiver" + ); + }, + _ => { + return Err(ContractError::NotOwner); + }, } - result?; - ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime end"); + ink::env::debug_println!("Contract::mint_through_runtime end"); Ok(()) } } diff --git a/pop-api/primitives/Cargo.toml b/pop-api/primitives/Cargo.toml index 80f0d929..ba8c6efd 100644 --- a/pop-api/primitives/Cargo.toml +++ b/pop-api/primitives/Cargo.toml @@ -6,6 +6,7 @@ version = "0.0.0" edition = "2021" [dependencies] +bounded-collections = { version = "0.1", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } @@ -17,6 +18,7 @@ crate-type = ["rlib"] [features] default = ["std"] std = [ + "bounded-collections/std", "scale/std", "scale-info/std", ] diff --git a/pop-api/primitives/src/lib.rs b/pop-api/primitives/src/lib.rs index b401dc5f..b3d84120 100644 --- a/pop-api/primitives/src/lib.rs +++ b/pop-api/primitives/src/lib.rs @@ -1,3 +1,18 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] +pub use bounded_collections::{BoundedBTreeMap, BoundedBTreeSet, BoundedVec, ConstU32}; +//use scale::{Decode, Encode, MaxEncodedLen}; + pub mod storage_keys; + +// /// Some way of identifying an account on the chain. +// #[derive(Encode, Decode, Debug, MaxEncodedLen)] +// pub struct AccountId([u8; 32]); +// Id used for identifying non-fungible collections. +pub type CollectionId = u32; +// Id used for identifying non-fungible items. +pub type ItemId = u32; +/// The maximum length of an attribute key. +pub type KeyLimit = ConstU32<64>; +/// The maximum approvals an item could have. +pub type ApprovalsLimit = ConstU32<20>; diff --git a/pop-api/primitives/src/storage_keys.rs b/pop-api/primitives/src/storage_keys.rs index c90851ab..dbb2b3c0 100644 --- a/pop-api/primitives/src/storage_keys.rs +++ b/pop-api/primitives/src/storage_keys.rs @@ -1,3 +1,4 @@ +use super::*; use scale::{Decode, Encode, MaxEncodedLen}; #[derive(Encode, Decode, Debug, MaxEncodedLen)] @@ -14,10 +15,20 @@ pub enum ParachainSystemKeys { // https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/nfts/src/impl_nonfungibles.rs #[derive(Encode, Decode, Debug, MaxEncodedLen)] pub enum NftsKeys { - Owner, - CollectionOwner, - Attribute, - CustomAttribute, - SystemAttribute, - CollectionAttribute, + // Get the details of a collection. + Collection(CollectionId), + /// Get the owner of the collection, if the collection exists. + CollectionOwner(CollectionId), + // Get the details of an item. + Item(CollectionId, ItemId), + /// Get the owner of the item, if the item exists. + Owner(CollectionId, ItemId), + /// Get the attribute value of `item` of `collection` corresponding to `key`. + Attribute(CollectionId, ItemId, BoundedVec), + // /// Get the custom attribute value of `item` of `collection` corresponding to `key`. + // CustomAttribute(AccountId, CollectionId, ItemId, BoundedVec), + /// Get the system attribute value of `item` of `collection` corresponding to `key` + SystemAttribute(CollectionId, Option, BoundedVec), + /// Get the attribute value of `item` of `collection` corresponding to `key`. + CollectionAttribute(CollectionId, BoundedVec), } diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index b8b47587..f1ffe3f5 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -11,17 +11,10 @@ pub use sp_runtime::{BoundedVec, MultiAddress, MultiSignature}; use v0::RuntimeCall; pub use v0::{balances, nfts, state}; -// Id used for identifying non-fungible collections. -pub type CollectionId = u32; - -// Id used for identifying non-fungible items. -pub type ItemId = u32; - type AccountId = ::AccountId; type Balance = ::Balance; type BlockNumber = ::BlockNumber; type StringLimit = u32; -type KeyLimit = u32; type MaxTips = u32; pub type Result = core::result::Result; diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs index ec75249e..b447aee8 100644 --- a/pop-api/src/v0/nfts.rs +++ b/pop-api/src/v0/nfts.rs @@ -1,7 +1,7 @@ use super::RuntimeCall; use crate::{PopApiError::UnknownStatusCode, *}; use ink::prelude::vec::Vec; -use primitives::MultiAddress; +use primitives::{ApprovalsLimit, BoundedBTreeMap, CollectionId, ItemId, KeyLimit, MultiAddress}; use scale::Encode; use types::*; @@ -300,6 +300,66 @@ pub fn claim_swap( }))?) } +/// Get the owner of the item, if the item exists. +pub fn owner(collection: CollectionId, item: ItemId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Owner(collection, item)))?) +} + +/// Get the owner of the collection, if the collection exists. +pub fn collection_owner(collection: CollectionId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionOwner(collection)))?) +} + +/// Get the attribute value of `item` of `collection` corresponding to `key`. +pub fn attribute( + collection: CollectionId, + item: ItemId, + key: BoundedVec, +) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Attribute(collection, item, key)))?) +} + +// /// Get the custom attribute value of `item` of `collection` corresponding to `key`. +// pub fn custom_attribute( +// account: AccountId, +// collection: CollectionId, +// item: ItemId, +// key: BoundedVec, +// ) -> Result>> { +// Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CustomAttribute( +// account, collection, item, key, +// )))?) +// } + +/// Get the system attribute value of `item` of `collection` corresponding to `key` if +/// `item` is `Some`. Otherwise, returns the system attribute value of `collection` +/// corresponding to `key`. +pub fn system_attribute( + collection: CollectionId, + item: Option, + key: BoundedVec, +) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::SystemAttribute(collection, item, key)))?) +} + +/// Get the attribute value of `item` of `collection` corresponding to `key`. +pub fn collection_attribute( + collection: CollectionId, + key: BoundedVec, +) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionAttribute(collection, key)))?) +} + +/// Get the details of a collection. +pub fn collection(collection: CollectionId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Collection(collection)))?) +} + +/// Get the details of an item. +pub fn item(collection: CollectionId, item: ItemId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Item(collection, item)))?) +} + #[derive(Encode)] pub(crate) enum NftCalls { #[codec(index = 0)] @@ -582,7 +642,10 @@ impl From for Error { // Local implementations of pallet-nfts types mod types { use super::*; - use crate::{Balance, BlockNumber, CollectionId}; + use crate::{ + primitives::{CollectionId, ItemId}, + Balance, BlockNumber, + }; use enumflags2::{bitflags, BitFlags}; use scale::{Decode, EncodeLike, MaxEncodedLen}; use scale_info::{build::Fields, meta_type, prelude::vec, Path, Type, TypeInfo, TypeParameter}; @@ -611,6 +674,24 @@ mod types { pub mint_settings: MintSettings, } + /// Information about a collection. + #[derive(Decode)] + pub struct CollectionDetails { + /// Collection's owner. + pub owner: AccountId, + /// The total balance deposited by the owner for all the storage data associated with this + /// collection. Used by `destroy`. + pub owner_deposit: Balance, + /// The total number of outstanding items of this collection. + pub items: u32, + /// The total number of outstanding item metadata of this collection. + pub item_metadatas: u32, + /// The total number of outstanding item configs of this collection. + pub item_configs: u32, + /// The total number of attributes for this collection. + pub attributes: u32, + } + /// Wrapper type for `BitFlags` that implements `Codec`. pub struct CollectionSettings(pub BitFlags); @@ -633,6 +714,18 @@ mod types { DepositRequired, } + /// Information concerning the ownership of a single unique item. + #[derive(Decode)] + pub struct ItemDetails { + /// The owner of this item. + pub owner: AccountId, + /// The approved transferrer of this item, if one is set. + pub approvals: BoundedBTreeMap, ApprovalsLimit>, + /// The amount held in the pallet's default account for this item. Free-hold items will have + /// this as zero. + pub deposit: Balance, + } + /// Support for up to 64 user-enabled features on an item. #[bitflags] #[repr(u64)] diff --git a/runtime/src/assets_config.rs b/runtime/src/assets_config.rs index e5aa144e..f2acffb0 100644 --- a/runtime/src/assets_config.rs +++ b/runtime/src/assets_config.rs @@ -38,7 +38,9 @@ parameter_types! { impl pallet_nfts::Config for Runtime { type RuntimeEvent = RuntimeEvent; + // TODO: source from primitives type CollectionId = CollectionId; + // TODO: source from primitives type ItemId = ItemId; type Currency = Balances; type CreateOrigin = AsEnsureOriginWithArg>; @@ -50,8 +52,10 @@ impl pallet_nfts::Config for Runtime { type AttributeDepositBase = NftsAttributeDepositBase; type DepositPerByte = NftsDepositPerByte; type StringLimit = ConstU32<256>; + // TODO: source from primitives type KeyLimit = ConstU32<64>; type ValueLimit = ConstU32<256>; + // TODO: source from primitives type ApprovalsLimit = ConstU32<20>; type ItemAttributesApprovalsLimit = ConstU32<30>; type MaxTips = ConstU32<10>; diff --git a/runtime/src/extensions.rs b/runtime/src/extensions.rs index 92663645..41f814eb 100644 --- a/runtime/src/extensions.rs +++ b/runtime/src/extensions.rs @@ -8,7 +8,10 @@ use log; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, }; -use pop_api_primitives::storage_keys::{NftsKeys, ParachainSystemKeys, RuntimeStateKeys}; +use pop_api_primitives::{ + storage_keys::{NftsKeys, ParachainSystemKeys, RuntimeStateKeys}, + CollectionId, ItemId, +}; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; @@ -21,7 +24,10 @@ pub struct PopApiExtension; impl ChainExtension for PopApiExtension where - T: pallet_contracts::Config + pallet_nfts::Config + cumulus_pallet_parachain_system::Config, + T: pallet_contracts::Config + + pallet_nfts::Config + + cumulus_pallet_parachain_system::Config + + frame_system::Config, ::AccountId: UncheckedFrom<::Hash> + AsRef<[u8]>, ::RuntimeCall: Parameter + Dispatchable::RuntimeOrigin, PostInfo = PostDispatchInfo> @@ -138,94 +144,10 @@ where } } -fn read_nfts_state( - key: NftsKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + pallet_nfts::Config + frame_system::Config, - E: Ext, -{ - match key { - NftsKeys::Owner => { - let (collection, item): ( - ::CollectionId, - ::ItemId, - ) = env.read_as()?; - - let maybe_owner = pallet_nfts::Pallet::::owner(collection, item); - Ok(maybe_owner.encode()) - }, - NftsKeys::CollectionOwner => { - let collection: ::CollectionId = env.read_as()?; - - let maybe_owner = pallet_nfts::Pallet::::collection_owner(collection); - - Ok(maybe_owner.encode()) - }, - NftsKeys::Attribute => { - // TODO: charge weight - let len = env.in_len(); - - let (collection, item, key): ( - ::CollectionId, - ::ItemId, - Vec, - ) = env.read_as_unbounded(len)?; - - let maybe_attribute = pallet_nfts::Pallet::::attribute(&collection, &item, &key); - - Ok(maybe_attribute.encode()) - }, - NftsKeys::CustomAttribute => { - // TODO: charge weight - let len = env.in_len(); - - let (account, collection, item, key): ( - ::AccountId, - ::CollectionId, - ::ItemId, - Vec, - ) = env.read_as_unbounded(len)?; - - let maybe_attribute = - pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key); - - Ok(maybe_attribute.encode()) - }, - NftsKeys::SystemAttribute => { - // TODO: charge weight - let len = env.in_len(); - - let (collection, maybe_item, key): ( - ::CollectionId, - Option<::ItemId>, - Vec, - ) = env.read_as_unbounded(len)?; - - let maybe_attribute = - pallet_nfts::Pallet::::system_attribute(&collection, maybe_item.as_ref(), &key); - - Ok(maybe_attribute.encode()) - }, - NftsKeys::CollectionAttribute => { - // TODO: charge weight - let len = env.in_len(); - - let (collection, key): (::CollectionId, Vec) = - env.read_as_unbounded(len)?; - - let maybe_attribute = pallet_nfts::Pallet::::collection_attribute(&collection, &key); - - Ok(maybe_attribute.encode()) - }, - } -} - fn read_state(env: Environment) -> Result<(), DispatchError> where T: pallet_contracts::Config - + pallet_nfts::Config + + pallet_nfts::Config + cumulus_pallet_parachain_system::Config + frame_system::Config, E: Ext, @@ -259,7 +181,7 @@ where Ok(relay_block_num.encode()) }, }, - } + }? .encode(); log::trace!( @@ -272,6 +194,52 @@ where }) } +fn read_nfts_state( + key: NftsKeys, + env: &mut Environment, +) -> Result, DispatchError> +where + T: pallet_contracts::Config + pallet_nfts::Config, + E: Ext, +{ + match key { + NftsKeys::Collection(collection) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Collection::::get(collection).encode()) + }, + NftsKeys::CollectionOwner(collection) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Pallet::::collection_owner(collection).encode()) + }, + NftsKeys::Item(collection, item) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Item::::get(collection, item).encode()) + }, + NftsKeys::Owner(collection, item) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Pallet::::owner(collection, item).encode()) + }, + NftsKeys::Attribute(collection, item, key) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Pallet::::attribute(&collection, &item, &key).encode()) + }, + // NftsKeys::CustomAttribute(account, collection, item, key) => { + // env.charge_weight(T::DbWeight::get().reads(1_u64))?; + // Ok(pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key) + // .encode()) + // }, + NftsKeys::SystemAttribute(collection, item, key) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Pallet::::system_attribute(&collection, item.as_ref(), &key) + .encode()) + }, + NftsKeys::CollectionAttribute(collection, key) => { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; + Ok(pallet_nfts::Pallet::::collection_attribute(&collection, &key).encode()) + }, + } +} + #[cfg(test)] mod tests { pub use super::*; From 954164750eeb97ad2720c7d3c9870dffba66642b Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 14:52:43 +0000 Subject: [PATCH 05/10] fix: add missing import --- runtime/src/extensions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/src/extensions.rs b/runtime/src/extensions.rs index 41f814eb..65df5031 100644 --- a/runtime/src/extensions.rs +++ b/runtime/src/extensions.rs @@ -14,6 +14,7 @@ use pop_api_primitives::{ }; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; +use sp_std::vec::Vec; const LOG_TARGET: &str = "pop-api::extension"; From 939a1a4d1730ac01be58ebcde086600a4ca85074 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 20:31:19 +0000 Subject: [PATCH 06/10] fix: increase visibility --- pop-api/src/v0/nfts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs index b447aee8..ae0a3a70 100644 --- a/pop-api/src/v0/nfts.rs +++ b/pop-api/src/v0/nfts.rs @@ -3,7 +3,7 @@ use crate::{PopApiError::UnknownStatusCode, *}; use ink::prelude::vec::Vec; use primitives::{ApprovalsLimit, BoundedBTreeMap, CollectionId, ItemId, KeyLimit, MultiAddress}; use scale::Encode; -use types::*; +pub use types::*; type Result = core::result::Result; From 3f244cfcd447e446e28ba889f772b51182665c76 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 21:05:24 +0000 Subject: [PATCH 07/10] fix: add missing derives --- pop-api/src/v0/nfts.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs index ae0a3a70..67055dad 100644 --- a/pop-api/src/v0/nfts.rs +++ b/pop-api/src/v0/nfts.rs @@ -1,7 +1,8 @@ use super::RuntimeCall; use crate::{PopApiError::UnknownStatusCode, *}; use ink::prelude::vec::Vec; -use primitives::{ApprovalsLimit, BoundedBTreeMap, CollectionId, ItemId, KeyLimit, MultiAddress}; +use primitives::{ApprovalsLimit, BoundedBTreeMap, KeyLimit, MultiAddress}; +pub use primitives::{CollectionId, ItemId}; use scale::Encode; pub use types::*; @@ -675,7 +676,7 @@ mod types { } /// Information about a collection. - #[derive(Decode)] + #[derive(Decode, Debug, Encode, Eq, PartialEq)] pub struct CollectionDetails { /// Collection's owner. pub owner: AccountId, @@ -715,7 +716,7 @@ mod types { } /// Information concerning the ownership of a single unique item. - #[derive(Decode)] + #[derive(Decode, Debug, Encode, Eq, PartialEq)] pub struct ItemDetails { /// The owner of this item. pub owner: AccountId, From 5a2ec5b387699a7dcbee31a030190d8d3023d341 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 21:12:53 +0000 Subject: [PATCH 08/10] feat(pop-api): add relay_chain_block_number function --- pop-api/src/lib.rs | 2 +- pop-api/src/v0/mod.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index f1ffe3f5..a1a88aee 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -9,7 +9,7 @@ use primitives::storage_keys::*; use scale; pub use sp_runtime::{BoundedVec, MultiAddress, MultiSignature}; use v0::RuntimeCall; -pub use v0::{balances, nfts, state}; +pub use v0::{balances, nfts, relay_chain_block_number, state}; type AccountId = ::AccountId; type Balance = ::Balance; diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 7b87dc25..0c20f9e6 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -1,7 +1,18 @@ +use crate::{ + primitives::storage_keys::{ParachainSystemKeys, RuntimeStateKeys}, + BlockNumber, PopApiError, +}; + pub mod balances; pub mod nfts; pub mod state; +pub fn relay_chain_block_number() -> Result { + Ok(state::read(RuntimeStateKeys::ParachainSystem( + ParachainSystemKeys::LastRelayChainBlockNumber, + ))?) +} + #[derive(scale::Encode)] pub(crate) enum RuntimeCall { #[codec(index = 10)] From 5deedcb0120aa71021cd6329cee987103244d173 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 22:14:26 +0000 Subject: [PATCH 09/10] refactor(nfts): group functions as per pallet --- pop-api/src/v0/nfts.rs | 538 ++++++++++++++++++++++------------------- 1 file changed, 286 insertions(+), 252 deletions(-) diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs index 67055dad..f4e0d4e4 100644 --- a/pop-api/src/v0/nfts.rs +++ b/pop-api/src/v0/nfts.rs @@ -54,21 +54,6 @@ pub fn redeposit(collection: CollectionId, items: Vec) -> Result<()> { Ok(dispatch(RuntimeCall::Nfts(NftCalls::Redeposit { collection, items }))?) } -/// Disallow further unprivileged transfer of an item. -pub fn lock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemTransfer { collection, item }))?) -} - -/// Re-allow unprivileged transfer of an item. -pub fn unlock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UnlockItemTransfer { collection, item }))?) -} - -/// Disallows specified settings for the whole collection. -pub fn lock_colelction(collection: CollectionId, lock_settings: CollectionSettings) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockCollection { collection, lock_settings }))?) -} - /// Change the Owner of a collection. pub fn transfer_ownership( collection: CollectionId, @@ -80,285 +65,334 @@ pub fn transfer_ownership( }))?) } -/// Change the Issuer, Admin and Freezer of a collection. -pub fn set_team( +/// Set (or reset) the acceptance of ownership for a particular account. +pub fn set_accept_ownership( collection: CollectionId, - issuer: Option>>, - admin: Option>>, - freezer: Option>>, + maybe_collection: Option, ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetTeam { - collection, - issuer: issuer.map(|i| i.into()), - admin: admin.map(|i| i.into()), - freezer: freezer.map(|i| i.into()), - }))?) + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAcceptOwnership { collection, maybe_collection }))?) } -/// Approve an item to be transferred by a delegated third-party account. -pub fn approve_transfer( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - maybe_deadline: Option, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveTransfer { - collection, - item, - delegate: delegate.into(), - maybe_deadline, - }))?) +/// Set the maximum number of items a collection could have. +pub fn set_collection_max_supply(collection: CollectionId, max_supply: u32) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMaxSupply { collection, max_supply }))?) } -/// Cancel one of the transfer approvals for a specific item. -pub fn cancel_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelApproval { - collection, - item, - delegate: delegate.into(), - }))?) +/// Update mint settings. +pub fn update_mint_settings(collection: CollectionId, mint_settings: MintSettings) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::UpdateMintSettings { collection, mint_settings }))?) } -/// Cancel all the approvals of a specific item. -pub fn clear_all_transfer_approvals(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAllTransferApprovals { collection, item }))?) +/// Get the owner of the item, if the item exists. +pub fn owner(collection: CollectionId, item: ItemId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Owner(collection, item)))?) } -/// Disallows changing the metadata or attributes of the item. -pub fn lock_item_properties( - collection: CollectionId, - item: ItemId, - lock_metadata: bool, - lock_attributes: bool, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemProperties { - collection, - item, - lock_metadata, - lock_attributes, - }))?) +/// Get the owner of the collection, if the collection exists. +pub fn collection_owner(collection: CollectionId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionOwner(collection)))?) } -/// Set an attribute for a collection or item. -pub fn set_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - value: BoundedVec, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAttribute { - collection, - maybe_item, - namespace, - key, - value, - }))?) +/// Get the details of a collection. +pub fn collection(collection: CollectionId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Collection(collection)))?) } -/// Clear an attribute for a collection or item. -pub fn clear_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAttribute { - collection, - maybe_item, - namespace, - key, - }))?) +/// Get the details of an item. +pub fn item(collection: CollectionId, item: ItemId) -> Result> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Item(collection, item)))?) } -/// Approve item's attributes to be changed by a delegated third-party account. -pub fn approve_item_attribute( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveItemAttribute { - collection, - item, - delegate: delegate.into(), - }))?) -} +pub mod approvals { + use super::*; -/// Cancel the previously provided approval to change item's attributes. -pub fn cancel_item_attributes_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelItemAttributesApproval { - collection, - item, - delegate: delegate.into(), - }))?) -} + /// Approve an item to be transferred by a delegated third-party account. + pub fn approve_transfer( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, + maybe_deadline: Option, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveTransfer { + collection, + item, + delegate: delegate.into(), + maybe_deadline, + }))?) + } -/// Set the metadata for an item. -pub fn set_metadata( - collection: CollectionId, - item: ItemId, - data: BoundedVec, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetMetadata { collection, item, data }))?) -} + /// Cancel one of the transfer approvals for a specific item. + pub fn cancel_approval( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelApproval { + collection, + item, + delegate: delegate.into(), + }))?) + } -/// Clear the metadata for an item. -pub fn clear_metadata(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearMetadata { collection, item }))?) + /// Cancel all the approvals of a specific item. + pub fn clear_all_transfer_approvals(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAllTransferApprovals { collection, item }))?) + } } -/// Set the metadata for a collection. -pub fn set_collection_metadata( - collection: CollectionId, - data: BoundedVec, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMetadata { collection, data }))?) -} +pub mod attributes { + use super::*; -/// Clear the metadata for a collection. -pub fn clear_collection_metadata(collection: CollectionId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearCollectionMetadata { collection }))?) -} + /// Approve item's attributes to be changed by a delegated third-party account. + pub fn approve_item_attribute( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveItemAttribute { + collection, + item, + delegate: delegate.into(), + }))?) + } -/// Set (or reset) the acceptance of ownership for a particular account. -pub fn set_accept_ownership( - collection: CollectionId, - maybe_collection: Option, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAcceptOwnership { collection, maybe_collection }))?) -} + /// Cancel the previously provided approval to change item's attributes. + pub fn cancel_item_attributes_approval( + collection: CollectionId, + item: ItemId, + delegate: impl Into>, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelItemAttributesApproval { + collection, + item, + delegate: delegate.into(), + }))?) + } -/// Set the maximum number of items a collection could have. -pub fn set_collection_max_supply(collection: CollectionId, max_supply: u32) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMaxSupply { collection, max_supply }))?) -} + /// Set an attribute for a collection or item. + pub fn set_attribute( + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, + value: BoundedVec, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAttribute { + collection, + maybe_item, + namespace, + key, + value, + }))?) + } -/// Update mint settings. -pub fn update_mint_settings(collection: CollectionId, mint_settings: MintSettings) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UpdateMintSettings { collection, mint_settings }))?) -} + /// Clear an attribute for a collection or item. + pub fn clear_attribute( + collection: CollectionId, + maybe_item: Option, + namespace: AttributeNamespace, + key: BoundedVec, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAttribute { + collection, + maybe_item, + namespace, + key, + }))?) + } -/// Set (or reset) the price for an item. -pub fn price(collection: CollectionId, item: ItemId, price: Option) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Price { collection, item, price }))?) -} + /// Get the attribute value of `item` of `collection` corresponding to `key`. + pub fn attribute( + collection: CollectionId, + item: ItemId, + key: BoundedVec, + ) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Attribute(collection, item, key)))?) + } -/// Allows to buy an item if it's up for sale. -pub fn buy_item(collection: CollectionId, item: ItemId, bid_price: Balance) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::BuyItem { collection, item, bid_price }))?) -} + // /// Get the custom attribute value of `item` of `collection` corresponding to `key`. + // pub fn custom_attribute( + // account: AccountId, + // collection: CollectionId, + // item: ItemId, + // key: BoundedVec, + // ) -> Result>> { + // Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CustomAttribute( + // account, collection, item, key, + // )))?) + // } + + /// Get the system attribute value of `item` of `collection` corresponding to `key` if + /// `item` is `Some`. Otherwise, returns the system attribute value of `collection` + /// corresponding to `key`. + pub fn system_attribute( + collection: CollectionId, + item: Option, + key: BoundedVec, + ) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::SystemAttribute(collection, item, key)))?) + } -/// Allows to pay the tips. -pub fn pay_tips(tips: BoundedVec) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::PayTips { tips }))?) + /// Get the attribute value of `item` of `collection` corresponding to `key`. + pub fn collection_attribute( + collection: CollectionId, + key: BoundedVec, + ) -> Result>> { + Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionAttribute(collection, key)))?) + } } -/// Register a new atomic swap, declaring an intention to send an `item` in exchange for -/// `desired_item` from origin to target on the current chain. -pub fn create_swap( - offered_collection: CollectionId, - offered_item: ItemId, - desired_collection: CollectionId, - maybe_desired_item: Option, - maybe_price: Option, - duration: BlockNumber, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CreateSwap { - offered_collection, - offered_item, - desired_collection, - maybe_desired_item, - maybe_price, - duration, - }))?) -} +pub mod locking { + use super::*; -/// Cancel an atomic swap. -pub fn cancel_swap(offered_collection: CollectionId, offered_item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelSwap { offered_collection, offered_item }))?) -} + /// Disallows changing the metadata or attributes of the item. + pub fn lock_item_properties( + collection: CollectionId, + item: ItemId, + lock_metadata: bool, + lock_attributes: bool, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemProperties { + collection, + item, + lock_metadata, + lock_attributes, + }))?) + } -/// Claim an atomic swap. -pub fn claim_swap( - send_collection: CollectionId, - send_item: ItemId, - receive_collection: CollectionId, - receive_item: ItemId, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClaimSwap { - send_collection, - send_item, - receive_collection, - receive_item, - }))?) -} + /// Disallow further unprivileged transfer of an item. + pub fn lock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemTransfer { collection, item }))?) + } -/// Get the owner of the item, if the item exists. -pub fn owner(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Owner(collection, item)))?) -} + /// Re-allow unprivileged transfer of an item. + pub fn unlock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::UnlockItemTransfer { collection, item }))?) + } -/// Get the owner of the collection, if the collection exists. -pub fn collection_owner(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionOwner(collection)))?) + /// Disallows specified settings for the whole collection. + pub fn lock_collection( + collection: CollectionId, + lock_settings: CollectionSettings, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockCollection { collection, lock_settings }))?) + } } -/// Get the attribute value of `item` of `collection` corresponding to `key`. -pub fn attribute( - collection: CollectionId, - item: ItemId, - key: BoundedVec, -) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Attribute(collection, item, key)))?) -} +pub mod metadata { + use super::*; -// /// Get the custom attribute value of `item` of `collection` corresponding to `key`. -// pub fn custom_attribute( -// account: AccountId, -// collection: CollectionId, -// item: ItemId, -// key: BoundedVec, -// ) -> Result>> { -// Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CustomAttribute( -// account, collection, item, key, -// )))?) -// } - -/// Get the system attribute value of `item` of `collection` corresponding to `key` if -/// `item` is `Some`. Otherwise, returns the system attribute value of `collection` -/// corresponding to `key`. -pub fn system_attribute( - collection: CollectionId, - item: Option, - key: BoundedVec, -) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::SystemAttribute(collection, item, key)))?) -} + /// Set the metadata for an item. + pub fn set_metadata( + collection: CollectionId, + item: ItemId, + data: BoundedVec, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetMetadata { collection, item, data }))?) + } -/// Get the attribute value of `item` of `collection` corresponding to `key`. -pub fn collection_attribute( - collection: CollectionId, - key: BoundedVec, -) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionAttribute(collection, key)))?) + /// Clear the metadata for an item. + pub fn clear_metadata(collection: CollectionId, item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearMetadata { collection, item }))?) + } + + /// Set the metadata for a collection. + pub fn set_collection_metadata( + collection: CollectionId, + data: BoundedVec, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMetadata { collection, data }))?) + } + + /// Clear the metadata for a collection. + pub fn clear_collection_metadata(collection: CollectionId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearCollectionMetadata { collection }))?) + } } -/// Get the details of a collection. -pub fn collection(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Collection(collection)))?) +pub mod roles { + use super::*; + + /// Change the Issuer, Admin and Freezer of a collection. + pub fn set_team( + collection: CollectionId, + issuer: Option>>, + admin: Option>>, + freezer: Option>>, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetTeam { + collection, + issuer: issuer.map(|i| i.into()), + admin: admin.map(|i| i.into()), + freezer: freezer.map(|i| i.into()), + }))?) + } } -/// Get the details of an item. -pub fn item(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Item(collection, item)))?) +pub mod trading { + use super::*; + + /// Allows to pay the tips. + pub fn pay_tips(tips: BoundedVec) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::PayTips { tips }))?) + } + + /// Set (or reset) the price for an item. + pub fn price(collection: CollectionId, item: ItemId, price: Option) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::Price { collection, item, price }))?) + } + + /// Allows to buy an item if it's up for sale. + pub fn buy_item(collection: CollectionId, item: ItemId, bid_price: Balance) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::BuyItem { collection, item, bid_price }))?) + } + + pub mod swaps { + use super::*; + + /// Register a new atomic swap, declaring an intention to send an `item` in exchange for + /// `desired_item` from origin to target on the current chain. + pub fn create_swap( + offered_collection: CollectionId, + offered_item: ItemId, + desired_collection: CollectionId, + maybe_desired_item: Option, + maybe_price: Option, + duration: BlockNumber, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CreateSwap { + offered_collection, + offered_item, + desired_collection, + maybe_desired_item, + maybe_price, + duration, + }))?) + } + + /// Cancel an atomic swap. + pub fn cancel_swap(offered_collection: CollectionId, offered_item: ItemId) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelSwap { + offered_collection, + offered_item, + }))?) + } + + /// Claim an atomic swap. + pub fn claim_swap( + send_collection: CollectionId, + send_item: ItemId, + receive_collection: CollectionId, + receive_item: ItemId, + ) -> Result<()> { + Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClaimSwap { + send_collection, + send_item, + receive_collection, + receive_item, + }))?) + } + } } #[derive(Encode)] From 16fa731363d7b45d771da31982ca34dd83508a91 Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Thu, 7 Mar 2024 22:26:12 +0000 Subject: [PATCH 10/10] chore: address clippy warnings --- pop-api/src/lib.rs | 1 - pop-api/src/v0/mod.rs | 4 +--- runtime/src/extensions.rs | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index a1a88aee..b33b6662 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -6,7 +6,6 @@ pub mod v0; use crate::PopApiError::{Balances, Nfts, UnknownStatusCode}; use ink::{prelude::vec::Vec, ChainExtensionInstance}; use primitives::storage_keys::*; -use scale; pub use sp_runtime::{BoundedVec, MultiAddress, MultiSignature}; use v0::RuntimeCall; pub use v0::{balances, nfts, relay_chain_block_number, state}; diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 0c20f9e6..a7d4e4b5 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -8,9 +8,7 @@ pub mod nfts; pub mod state; pub fn relay_chain_block_number() -> Result { - Ok(state::read(RuntimeStateKeys::ParachainSystem( - ParachainSystemKeys::LastRelayChainBlockNumber, - ))?) + state::read(RuntimeStateKeys::ParachainSystem(ParachainSystemKeys::LastRelayChainBlockNumber)) } #[derive(scale::Encode)] diff --git a/runtime/src/extensions.rs b/runtime/src/extensions.rs index 65df5031..84f359c1 100644 --- a/runtime/src/extensions.rs +++ b/runtime/src/extensions.rs @@ -4,7 +4,6 @@ use frame_support::{ pallet_prelude::*, traits::nonfungibles_v2::Inspect, }; -use log; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, SysConfig, };