Skip to content

Commit

Permalink
Added updated nft_pallet
Browse files Browse the repository at this point in the history
  • Loading branch information
ndkazu committed Aug 16, 2023
1 parent 36fe397 commit bed8ebf
Show file tree
Hide file tree
Showing 12 changed files with 1,537 additions and 47 deletions.
126 changes: 84 additions & 42 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions node/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use fs_node_runtime::{
RolesModuleConfig,AccountId, AuraConfig, BalancesConfig, CouncilConfig,BackgroundCouncilConfig,RuntimeGenesisConfig, GrandpaConfig, Signature, SudoConfig,
pallet_roles,RolesModuleConfig,AccountId, AuraConfig, BalancesConfig, NftModuleConfig,CouncilConfig,BackgroundCouncilConfig,RuntimeGenesisConfig, GrandpaConfig, Signature, SudoConfig,
SystemConfig, WASM_BINARY,
};
use sc_service::{ChainType, Properties};
Expand Down Expand Up @@ -255,6 +255,12 @@ fn square_one(
representatives: vec![
],
},
nft_module: NftModuleConfig {
owner: Some(root_key),
collection_id: Some(3),
created_by: Some(pallet_roles::Accounts::SERVICER),
metadata: Some(b"metadata".to_vec().try_into().unwrap()),
},
democracy: Default::default(),
treasury: Default::default(),
council: CouncilConfig {
Expand Down Expand Up @@ -304,14 +310,20 @@ fn testnet_genesis(
},
sudo: SudoConfig {
// Assign network admin rights.
key: Some(root_key),
key: Some(root_key.clone()),
},
transaction_payment: Default::default(),

roles_module: RolesModuleConfig {
new_admin: Some(hex!["2a0170a78af6835dd46753c1857b31903aa125d9c203e05bc7a45b7c3bea702b"].into()),
representatives: vec![],
},
nft_module: NftModuleConfig {
owner: Some(root_key),
collection_id: Some(3),
created_by: Some(pallet_roles::Accounts::SERVICER),
metadata: Some(b"metadata".to_vec().try_into().unwrap()),
},
democracy: Default::default(),
treasury: Default::default(),
council: CouncilConfig {
Expand Down
59 changes: 59 additions & 0 deletions pallets/nft/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[package]
name = "pallet-nft"
version = "4.0.0"
description = "Nft management Pallet"
authors = ["Fair Squares"]
homepage = "https://fair-squares.nl"
edition = "2021"
license = "Apache 2.0"
publish = false
repository = "https://github.com/Fair-Squares/fair-squares"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
enum-iterator = "1.4.1"
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [
"derive",
] }
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0"}
frame-system = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", optional = true }
pallet-sudo = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
pallet-uniques = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
pallet-roles = { default-features = false, path="../roles" }
sp-std = { default-features = false, version = "8.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-runtime = { default-features = false, version = "24.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
serde = { version = "1.0.183", optional = true, features = ["derive"] }

[dev-dependencies]
sp-core = { default-features = false, version = "21.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-io = { default-features = false, version = "23.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-runtime = { default-features = false, version = "24.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }

[features]
default = ["std"]
std = [
"serde/std",
"codec/std",
"scale-info/std",
"pallet-balances/std",
"frame-support/std",
"frame-system/std",
"frame-benchmarking/std",
"pallet-sudo/std",
"pallet-uniques/std",
"sp-std/std",
"pallet-roles/std",
]

runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]

295 changes: 295 additions & 0 deletions pallets/nft/src/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
pub use super::*;
pub use frame_system::{pallet_prelude::OriginFor, RawOrigin};

pub trait CreateTypedCollection<AccountId, CollectionId>: Create<AccountId> {
/// This function create an NFT collection of `created_by` type.
fn create_typed_collection(owner: AccountId, collection_id: CollectionId) -> DispatchResult;
}

pub trait ReserveCollectionId<CollectionId> {
/// This function returns `true` if collection id is from the reserved range, `false` otherwise.
fn is_id_reserved(id: CollectionId) -> bool;
}

impl<T: Config> Pallet<T> {
pub fn collection_owner(collection_id: T::NftCollectionId) -> Option<T::AccountId> {
pallet_uniques::Pallet::<T>::collection_owner(collection_id.into())
}

pub fn owner(collection_id: T::NftCollectionId, item_id: T::NftItemId) -> Option<T::AccountId> {
pallet_uniques::Pallet::<T>::owner(collection_id.into(), item_id.into())
}

pub fn do_create_collection(
owner: T::AccountId,
collection_id: T::NftCollectionId,
created_by: Acc,
metadata: BoundedVecOfUnq<T>,
) -> DispatchResult {
let deposit_info = match T::Permissions::has_deposit(&created_by) {
false => (Zero::zero(), true),
true => (T::CollectionDeposit::get(), false),
};
pallet_uniques::Pallet::<T>::do_create_collection(
collection_id.into(),
owner.clone(),
owner.clone(),
deposit_info.0,
deposit_info.1,
pallet_uniques::Event::Created {
collection: collection_id.into(),
creator: owner.clone(),
owner: owner.clone(),
},
)?;

Collections::<T>::insert(collection_id, CollectionInfo { created_by, metadata });

Self::deposit_event(Event::CollectionCreated { owner, collection_id, created_by });

Ok(())
}

pub fn do_mint(
owner: T::AccountId,
collection_id: T::NftCollectionId,
item_id: T::NftItemId,
metadata: BoundedVecOfUnq<T>,
) -> DispatchResult {
ensure!(Collections::<T>::contains_key(collection_id), Error::<T>::CollectionUnknown);
pallet_uniques::Pallet::<T>::do_mint(
collection_id.into(),
item_id.into(),
owner.clone(),
|_details| Ok(()),
)?;

Items::<T>::insert(collection_id, item_id, ItemInfo { metadata });

Self::deposit_event(Event::ItemMinted { owner, collection_id, item_id });

Ok(())
}

pub fn set_metadata(
owner: T::AccountId,
collection_id: T::NftCollectionId,
item_id: T::NftItemId,
metadata: BoundedVecOfUnq<T>,
) -> DispatchResult {
ensure!(Collections::<T>::contains_key(collection_id), Error::<T>::CollectionUnknown);
let origin = RawOrigin::Signed(owner);

let res0 = pallet_uniques::Pallet::<T>::set_metadata(
origin.into(),
collection_id.into(),
item_id.into(),
metadata.clone(),
false,
);
debug_assert!(res0.is_ok());

Items::<T>::mutate(collection_id, item_id, |val| {
let mut val0 = val.clone().unwrap();
val0.metadata = metadata;
*val = Some(val0);
});

//Self::deposit_event(Event::ItemMinted { owner, collection_id, item_id });

Ok(())
}

pub fn do_transfer(
collection_id: T::NftCollectionId,
item_id: T::NftItemId,
from: T::AccountId,
to: T::AccountId,
) -> DispatchResult {
if from == to {
return Ok(())
}

pallet_uniques::Pallet::<T>::do_transfer(
collection_id.into(),
item_id.into(),
to.clone(),
|_collection_details, _item_details| {
Self::deposit_event(Event::ItemTransferred { from, to, collection_id, item_id });
Ok(())
},
)
}

pub fn do_burn(
owner: T::AccountId,
collection_id: T::NftCollectionId,
item_id: T::NftItemId,
) -> DispatchResult {
pallet_uniques::Pallet::<T>::do_burn(
collection_id.into(),
item_id.into(),
|_collection_details, _item_details| Ok(()),
)?;

Items::<T>::remove(collection_id, item_id);

Self::deposit_event(Event::ItemBurned { owner, collection_id, item_id });

Ok(())
}

pub fn do_destroy_collection(
owner: T::AccountId,
collection_id: T::NftCollectionId,
) -> DispatchResult {
let witness = pallet_uniques::Pallet::<T>::get_destroy_witness(&collection_id.into())
.ok_or(Error::<T>::CollectionUnknown)?;

// witness struct is empty because we don't allow destroying a Collection with existing
// items
ensure!(witness.items == 0u32, Error::<T>::TokenCollectionNotEmpty);

pallet_uniques::Pallet::<T>::do_destroy_collection(
collection_id.into(),
witness,
Some(owner.clone()),
)?;
Collections::<T>::remove(collection_id);

Self::deposit_event(Event::CollectionDestroyed { owner, collection_id });
Ok(())
}
}

impl<T: Config> Inspect<T::AccountId> for Pallet<T> {
type ItemId = T::NftItemId;
type CollectionId = T::NftCollectionId;

fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option<T::AccountId> {
Self::owner(*collection, *item)
}

fn collection_owner(collection: &Self::CollectionId) -> Option<T::AccountId> {
Self::collection_owner(*collection)
}
}

impl<T: Config> InspectEnumerable<T::AccountId> for Pallet<T> {
type CollectionsIterator = Box<dyn Iterator<Item = <T as Config>::NftCollectionId>>;
type ItemsIterator = Box<dyn Iterator<Item = <T as Config>::NftItemId>>;
type OwnedIterator = Box<dyn Iterator<Item = (<T as Config>::NftCollectionId, <T as Config>::NftItemId)>>;
type OwnedInCollectionIterator = Box<dyn Iterator<Item = <T as Config>::NftItemId>>;

/// Returns an iterator of the collections in existence.
fn collections() -> Self::CollectionsIterator {
Box::new(Collections::<T>::iter_keys())
}

/// Returns an iterator of the items of a `collection` in existence.
fn items(collection: &Self::CollectionId) -> Self::ItemsIterator {
Box::new(Items::<T>::iter_key_prefix(collection))
}

/// Returns an iterator of the items of all collections owned by `who`.
fn owned(who: &T::AccountId) -> Self::OwnedIterator {
Box::new(
pallet_uniques::Pallet::<T>::owned(who)
.map(|(collection_id, item_id)| (collection_id.into(), item_id.into())),
)
}

/// Returns an iterator of the items of `collection` owned by `who`.
fn owned_in_collection(collection: &Self::CollectionId, who: &T::AccountId) -> Self::OwnedInCollectionIterator {
Box::new(
pallet_uniques::Pallet::<T>::owned_in_collection(
&(Into::<<T as pallet_uniques::Config>::CollectionId>::into(*collection)),
who,
)
.map(|i| i.into()),
)
}
}


impl<T: Config> Create<T::AccountId> for Pallet<T> {
fn create_collection(
collection: &Self::CollectionId,
who: &T::AccountId,
_admin: &T::AccountId,
) -> DispatchResult {
Self::do_create_collection(
who.clone(),
*collection,
Default::default(),
BoundedVec::default(),
)?;

Ok(())
}
}

impl<T: Config> Destroy<T::AccountId> for Pallet<T> {
type DestroyWitness = pallet_uniques::DestroyWitness;

fn get_destroy_witness(collection: &Self::CollectionId) -> Option<Self::DestroyWitness> {
pallet_uniques::Pallet::<T>::get_destroy_witness(
&(Into::<<T as pallet_uniques::Config>::CollectionId>::into(*collection)),
)
}

fn destroy(
collection: Self::CollectionId,
_witness: Self::DestroyWitness,
_maybe_check_owner: Option<T::AccountId>,
) -> Result<Self::DestroyWitness, DispatchError> {
let owner = Self::collection_owner(collection).ok_or(Error::<T>::CollectionUnknown)?;

Self::do_destroy_collection(owner, collection)?;

// We can return empty struct here because we don't allow destroying a Collection with
// existing items
Ok(DestroyWitness { items: 0, item_metadatas: 0, attributes: 0 })
}
}

impl<T: Config> Mutate<T::AccountId> for Pallet<T> {
fn mint_into(
collection: &Self::CollectionId,
item: &Self::ItemId,
who: &T::AccountId,
) -> DispatchResult {
Self::do_mint(who.clone(), *collection, *item, BoundedVec::default())?;

Ok(())
}

fn burn(
collection: &Self::CollectionId,
item: &Self::ItemId,
_maybe_check_owner: Option<&T::AccountId>,
) -> DispatchResult {
let owner = Self::owner(*collection, *item).ok_or(Error::<T>::ItemUnknown)?;

Self::do_burn(owner, *collection, *item)?;

Ok(())
}
}

impl<T: Config> CreateTypedCollection<T::AccountId, T::NftCollectionId> for Pallet<T> {
fn create_typed_collection(
owner: T::AccountId,
collection_id: T::NftCollectionId,
) -> DispatchResult {
let created_by = Roles::Pallet::<T>::get_roles(&owner)[0];
ensure!(T::Permissions::can_create(&created_by), Error::<T>::NotPermitted);
Self::do_create_collection(owner, collection_id, created_by, Default::default())
}
}

impl<T: Config> ReserveCollectionId<T::NftCollectionId> for Pallet<T> {
fn is_id_reserved(id: T::NftCollectionId) -> bool {
id == T::ReserveCollectionIdUpTo::get()
}
}
Loading

0 comments on commit bed8ebf

Please sign in to comment.