Skip to content

Commit

Permalink
feat(pop-api): further implementation of nfts api (#21)
Browse files Browse the repository at this point in the history
* fix: restore missing ink contract attribute

* feat(pop-api): further implementation of nfts api

* feat(pop-api): add support to query nft state in extension

* feat(pop-api): continue support for nft state queries

* fix: add missing import

* fix: increase visibility

* fix: add missing derives

* feat(pop-api): add relay_chain_block_number function

* refactor(nfts): group functions as per pallet

* chore: address clippy warnings

---------

Co-authored-by: Peter White <petras9789@gmail.com>
  • Loading branch information
evilrobot-01 and peterwht committed Mar 7, 2024
1 parent cd57408 commit a87e19d
Show file tree
Hide file tree
Showing 11 changed files with 819 additions and 135 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

107 changes: 62 additions & 45 deletions contracts/pop-api-examples/nfts/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,80 @@ 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),
InvalidCollection,
ItemAlreadyExists,
NftsError(nfts::Error),
NotOwner,
}

impl From<nfts::Error> 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!("Contract::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!(
"Contract::mint_through_runtime: collection_id: {:?} item_id {:?} receiver: {:?}",
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?;
// 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);
}

ink::env::debug_println!("PopApiExtensionDemo::mint_through_runtime end");
Ok(())
}
}
// mint api
pop_api::nfts::mint(collection_id, item_id, receiver)?;
ink::env::debug_println!("Contract::mint_through_runtime: item minted successfully");

#[cfg(test)]
mod tests {
use super::*;
// 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);
},
}

#[ink::test]
fn default_works() {
PopApiExtensionDemo::new();
}
}
ink::env::debug_println!("Contract::mint_through_runtime end");
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[ink::test]
fn default_works() {
PopApiExtensionDemo::new();
}
}
}
4 changes: 3 additions & 1 deletion pop-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand All @@ -22,6 +23,7 @@ crate-type = ["rlib"]
[features]
default = ["std"]
std = [
"enumflags2/std",
"ink/std",
"pop-api-primitives/std",
"scale/std",
Expand Down
2 changes: 2 additions & 0 deletions pop-api/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand All @@ -17,6 +18,7 @@ crate-type = ["rlib"]
[features]
default = ["std"]
std = [
"bounded-collections/std",
"scale/std",
"scale-info/std",
]
15 changes: 15 additions & 0 deletions pop-api/primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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>;
23 changes: 23 additions & 0 deletions pop-api/primitives/src/storage_keys.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
use super::*;
use scale::{Decode, Encode, MaxEncodedLen};

#[derive(Encode, Decode, Debug, MaxEncodedLen)]
pub enum RuntimeStateKeys {
Nfts(NftsKeys),
ParachainSystem(ParachainSystemKeys),
}

#[derive(Encode, Decode, Debug, MaxEncodedLen)]
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 {
// 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<u8, KeyLimit>),
// /// Get the custom attribute value of `item` of `collection` corresponding to `key`.
// CustomAttribute(AccountId, CollectionId, ItemId, BoundedVec<u8, KeyLimit>),
/// Get the system attribute value of `item` of `collection` corresponding to `key`
SystemAttribute(CollectionId, Option<ItemId>, BoundedVec<u8, KeyLimit>),
/// Get the attribute value of `item` of `collection` corresponding to `key`.
CollectionAttribute(CollectionId, BoundedVec<u8, KeyLimit>),
}
16 changes: 4 additions & 12 deletions pop-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,14 @@ 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, state};
pub use v0::{balances, nfts, relay_chain_block_number, 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 = <ink::env::DefaultEnvironment as ink::env::Environment>::AccountId;
type Balance = <ink::env::DefaultEnvironment as ink::env::Environment>::Balance;
type BlockNumber = <ink::env::DefaultEnvironment as ink::env::Environment>::BlockNumber;
type AccountId = <Environment as ink::env::Environment>::AccountId;
type Balance = <Environment as ink::env::Environment>::Balance;
type BlockNumber = <Environment as ink::env::Environment>::BlockNumber;
type StringLimit = u32;
type KeyLimit = u32;
type MaxTips = u32;

pub type Result<T> = core::result::Result<T, PopApiError>;
Expand Down
9 changes: 9 additions & 0 deletions pop-api/src/v0/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
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<BlockNumber, PopApiError> {
state::read(RuntimeStateKeys::ParachainSystem(ParachainSystemKeys::LastRelayChainBlockNumber))
}

#[derive(scale::Encode)]
pub(crate) enum RuntimeCall {
#[codec(index = 10)]
Expand Down
Loading

0 comments on commit a87e19d

Please sign in to comment.