difftreelog
Merge pull request #5 from usetech-llc/hotfix/collection_count_update
in: master
Hotfix/collection count update
4 files changed
README.mddiffbeforeafterboth123# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the8basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of9a given Collection, and managing their ownership.1011The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a12concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge13about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1415The NFT Chain also provides:1617- Smart Contracts Pallet and example smart contract that interacts with NFT Runtime18- ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split19 between multiple users)20- Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract21 users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection22 Transfer transactions.2324Wider NFT Ecosystem (most of it was developed during Hackusama):2526- [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)27- [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)28- [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2930Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!3132## Hackusama Update3334During the Kusama Hackaphon the following changes were made:3536- Enabled Smart Contracts Pallet37- Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)38- Fixed misc. bugs in NFT Pallet39- Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.40- New Features:41 - Re-Fungible Token Mode42 - Off-Chain Schema to store token image URLs43 - Alternative economic model44 - White Lists and Public Mint Permission45- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet46 Blockchain.4748## Application Development4950If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).5152## Building5354Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything55so that we can keep the builds stable.56571. Install Rust:5859```bash60curl https://sh.rustup.rs -sSf | sh61sudo apt-get install libssl-dev pkg-config libclang-dev clang62```63642. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.65663. Install Rust Toolchain 1.44.0:6768```bash69rustup install 1.44.070```71724. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)7374```bash75rustup toolchain list76rustup default 1.44.0-x86_64-unknown-linux-gnu77```78795. Install nightly toolchain and add wasm target for it:8081```bash82rustup toolchain install nightly-2020-05-0183rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu84```85866. Build:8788```bash89cargo build90```9192## Run9394You can start a development chain with:9596```bash97cargo run -- --dev98```99100Detailed logs may be shown by running the node with the following environment variables set:101`RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.102103If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two104validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with105testnet units. Give each node a name and expose them so they are listed on the Polkadot106[telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.107108We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at109`/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from110the `--node-key` value that we specify below:111112```bash113cargo run -- \114 --base-path /tmp/alice \115 --chain=local \116 --alice \117 --node-key 0000000000000000000000000000000000000000000000000000000000000001 \118 --telemetry-url ws://telemetry.polkadot.io:1024 \119 --validator120```121122In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database123stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's124bootnode ID on TCP port 30333:125126```bash127cargo run -- \128 --base-path /tmp/bob \129 --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \130 --chain=local \131 --bob \132 --port 30334 \133 --telemetry-url ws://telemetry.polkadot.io:1024 \134 --validator135```136137Additional CLI usage options are available and may be shown by running `cargo run -- --help`.138139## UI custom types140141```142{143 "Schedule": {144 "version": "u32",145 "put_code_per_byte_cost": "Gas",146 "grow_mem_cost": "Gas",147 "regular_op_cost": "Gas",148 "return_data_per_byte_cost": "Gas",149 "event_data_per_byte_cost": "Gas",150 "event_per_topic_cost": "Gas",151 "event_base_cost": "Gas",152 "call_base_cost": "Gas",153 "instantiate_base_cost": "Gas",154 "dispatch_base_cost": "Gas",155 "sandbox_data_read_cost": "Gas",156 "sandbox_data_write_cost": "Gas",157 "transfer_cost": "Gas",158 "instantiate_cost": "Gas",159 "max_event_topics": "u32",160 "max_stack_height": "u32",161 "max_memory_pages": "u32",162 "max_table_size": "u32",163 "enable_println": "bool",164 "max_subject_len": "u32"165 },166 "CollectionMode": {167 "_enum": {168 "Invalid": null,169 "NFT": "u32",170 "Fungible": "u32",171 "ReFungible": "(u32, u32)"172 }173 },174 "Ownership": {175 "Owner": "AccountId",176 "Fraction": "u128"177 },178 "FungibleItemType": {179 "Collection": "u64",180 "Owner": "AccountId",181 "Value": "u128"182 },183 "ReFungibleItemType": {184 "Collection": "u64",185 "Owner": "Vec<Ownership>",186 "Data": "Vec<u8>"187 },188 "NftItemType": {189 "Collection": "u64",190 "Owner": "AccountId",191 "Data": "Vec<u8>"192 },193 "Ownership": {194 "owner": "AccountId",195 "fraction": "u128"196 },197 "ReFungibleItemType": {198 "Collection": "u64",199 "Owner": "Vec<Ownership<AccountId>>",200 "Data": "Vec<u8>"201 },202 "CollectionType": {203 "Owner": "AccountId",204 "Mode": "CollectionMode",205 "Access": "u8",206 "DecimalPoints": "u32",207 "Name": "Vec<u16>",208 "Description": "Vec<u16>",209 "TokenPrefix": "Vec<u8>",210 "CustomDataSize": "u32",211 "OffchainSchema": "Vec<u8>",212 "Sponsor": "AccountId",213 "UnconfirmedSponsor": "AccountId"214 },215 "RawData": "Vec<u8>",216 "Address": "AccountId",217 "LookupSource": "AccountId",218 "Weight": "u64"219}220```123# NFT Parachain45## Project Description67The NFT Pallet is the core of NFT functionality. Like ERC-721 standard in Ethereum ecosystem, this pallet provides the8basement for creating collections of unique non-divisible things, also called Non Fungible Tokens (NFTs), minting NFT of9a given Collection, and managing their ownership.1011The pallet also enables storing NFT properties. Though (according to ERC-721) NFT properties belong to logic of a12concrete application that operates a Collection, so purposefully the NFT Tracking Module does not have any knowledge13about properties except their byte size leaving application logic out to be controlled by Smart Contracts.1415The NFT Chain also provides:1617- Smart Contracts Pallet and example smart contract that interacts with NFT Runtime18- ERC-1155 Functionality (currently PoC as Re-Fungible tokens, i.e. items that are still unique, but that can be split19 between multiple users)20- Variety of economic options for dapp producers to choose from to create freemium games and other ways to attract21 users. As a step one, we implemented an economic model when a collection sponsor can be set to pay for collection22 Transfer transactions.2324Wider NFT Ecosystem (most of it was developed during Hackusama):2526- [SubstraPunks Game hosted on IPFS](https://github.com/usetech-llc/substrapunks)27- [NFT Wallet and UI](https://uniqueapps.usetech.com/#/nft)28- [NFT Asset for Unity Framework](https://github.com/usetech-llc/nft_unity)2930Please see our [walk-thorugh instructions](doc/hackusama_walk_through.md) to try everything out!3132## Hackusama Update3334During the Kusama Hackaphon the following changes were made:3536- Enabled Smart Contracts Pallet37- Enabled integration between Smart Contracts and NFT Pallet (required special edition of RC4 Substrate version)38- Fixed misc. bugs in NFT Pallet39- Deployed NFT TestNet. Public node available at wss://unique.usetech.com, custom UI types - see below in this README.40- New Features:41 - Re-Fungible Token Mode42 - Off-Chain Schema to store token image URLs43 - Alternative economic model44 - White Lists and Public Mint Permission45- Use example: [SubstraPunks Game](https://github.com/usetech-llc/substrapunks), fully hosted on IPFS and NFT Testnet46 Blockchain.4748## Application Development4950If you are building an application that operates NFT tokens, use [this document](doc/application_development.md).5152## Building5354Building NFT chain requires special versions of Rust and toolchain. We don't use the most recent versions of everything55so that we can keep the builds stable.56571. Install Rust:5859```bash60curl https://sh.rustup.rs -sSf | sh61sudo apt-get install libssl-dev pkg-config libclang-dev clang62```63642. Remove all installed toolchains with `rustup toolchain list` and `rustup toolchain uninstall <toolchain>`.65663. Install Rust Toolchain 1.44.0:6768```bash69rustup install 1.44.070```71724. Make it default (actual toochain version may be different, so do a `rustup toolchain list` first)7374```bash75rustup toolchain list76rustup default 1.44.0-x86_64-unknown-linux-gnu77```78795. Install nightly toolchain and add wasm target for it:8081```bash82rustup toolchain install nightly-2020-05-0183rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-01-x86_64-unknown-linux-gnu84```85866. Build:8788```bash89cargo build90```9192## Run9394You can start a development chain with:9596```bash97cargo run -- --dev98```99100Detailed logs may be shown by running the node with the following environment variables set:101`RUST_LOG=debug RUST_BACKTRACE=1 cargo run -- --dev`.102103If you want to see the multi-node consensus algorithm in action locally, then you can create a local testnet with two104validator nodes for Alice and Bob, who are the initial authorities of the genesis chain that have been endowed with105testnet units. Give each node a name and expose them so they are listed on the Polkadot106[telemetry site](https://telemetry.polkadot.io/#/Local%20Testnet). You'll need two terminal windows open.107108We'll start Alice's substrate node first on default TCP port 30333 with her chain database stored locally at109`/tmp/alice`. The bootnode ID of her node is `QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN`, which is generated from110the `--node-key` value that we specify below:111112```bash113cargo run -- \114 --base-path /tmp/alice \115 --chain=local \116 --alice \117 --node-key 0000000000000000000000000000000000000000000000000000000000000001 \118 --telemetry-url ws://telemetry.polkadot.io:1024 \119 --validator120```121122In the second terminal, we'll start Bob's substrate node on a different TCP port of 30334, and with his chain database123stored locally at `/tmp/bob`. We'll specify a value for the `--bootnodes` option that will connect his node to Alice's124bootnode ID on TCP port 30333:125126```bash127cargo run -- \128 --base-path /tmp/bob \129 --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/QmQZ8TjTqeDj3ciwr93EJ95hxfDsb9pEYDizUAbWpigtQN \130 --chain=local \131 --bob \132 --port 30334 \133 --telemetry-url ws://telemetry.polkadot.io:1024 \134 --validator135```136137Additional CLI usage options are available and may be shown by running `cargo run -- --help`.138139## UI custom types140141```142{143 "Schedule": {144 "version": "u32",145 "put_code_per_byte_cost": "Gas",146 "grow_mem_cost": "Gas",147 "regular_op_cost": "Gas",148 "return_data_per_byte_cost": "Gas",149 "event_data_per_byte_cost": "Gas",150 "event_per_topic_cost": "Gas",151 "event_base_cost": "Gas",152 "call_base_cost": "Gas",153 "instantiate_base_cost": "Gas",154 "dispatch_base_cost": "Gas",155 "sandbox_data_read_cost": "Gas",156 "sandbox_data_write_cost": "Gas",157 "transfer_cost": "Gas",158 "instantiate_cost": "Gas",159 "max_event_topics": "u32",160 "max_stack_height": "u32",161 "max_memory_pages": "u32",162 "max_table_size": "u32",163 "enable_println": "bool",164 "max_subject_len": "u32"165 },166 "CollectionMode": {167 "_enum": {168 "Invalid": null,169 "NFT": "u32",170 "Fungible": "u32",171 "ReFungible": "(u32, u32)"172 }173 },174 "Ownership": {175 "Owner": "AccountId",176 "Fraction": "u128"177 },178 "FungibleItemType": {179 "Collection": "u64",180 "Owner": "AccountId",181 "Value": "u128"182 },183 "ReFungibleItemType": {184 "Collection": "u64",185 "Owner": "Vec<Ownership>",186 "Data": "Vec<u8>"187 },188 "NftItemType": {189 "Collection": "u64",190 "Owner": "AccountId",191 "Data": "Vec<u8>"192 },193 "Ownership": {194 "owner": "AccountId",195 "fraction": "u128"196 },197 "ReFungibleItemType": {198 "Collection": "u64",199 "Owner": "Vec<Ownership<AccountId>>",200 "Data": "Vec<u8>"201 },202 "CollectionType": {203 "Owner": "AccountId",204 "Mode": "CollectionMode",205 "Access": "u8",206 "DecimalPoints": "u32",207 "Name": "Vec<u16>",208 "Description": "Vec<u16>",209 "TokenPrefix": "Vec<u8>",210 "CustomDataSize": "u32",211 "MintMode": "bool",212 "OffchainSchema": "Vec<u8>",213 "Sponsor": "AccountId",214 "UnconfirmedSponsor": "AccountId"215 },216 "RawData": "Vec<u8>",217 "Address": "AccountId",218 "LookupSource": "AccountId",219 "Weight": "u64"220}221```pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -97,6 +97,7 @@
pub description: Vec<u16>, // 256 include null escape char
pub token_prefix: Vec<u8>, // 16 include null escape char
pub custom_data_size: u32,
+ pub mint_mode: bool,
pub offchain_schema: Vec<u8>,
pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship
@@ -178,9 +179,6 @@
pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;
pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;
pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;
-
- // Active vesting list
- // pub VestingList get(fn vesting): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => VestingItem<T::AccountId, T::Moment>;
/// Index list
pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;
@@ -209,7 +207,7 @@
fn on_initialize(now: T::BlockNumber) -> Weight {
- if ChainVersion::get() == 0
+ if ChainVersion::get() < 2
{
let value = NextCollectionID::get();
CreatedCollectionCount::put(value);
@@ -224,11 +222,11 @@
// @param customDataSz size of custom data in each collection item
// returns collection ID
#[weight = 0]
- pub fn create_collection( origin,
- collection_name: Vec<u16>,
- collection_description: Vec<u16>,
- token_prefix: Vec<u8>,
- mode: CollectionMode) -> DispatchResult {
+ pub fn create_collection(origin,
+ collection_name: Vec<u16>,
+ collection_description: Vec<u16>,
+ token_prefix: Vec<u8>,
+ mode: CollectionMode) -> DispatchResult {
// Anyone can create a collection
let who = ensure_signed(origin)?;
@@ -271,6 +269,7 @@
owner: who.clone(),
name: name,
mode: mode.clone(),
+ mint_mode: false,
access: AccessMode::Normal,
description: description,
decimal_points: decimal_points,
@@ -309,6 +308,73 @@
}
#[weight = 0]
+ pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+
+ let sender = ensure_signed(origin)?;
+ Self::check_owner_or_admin_permissions(collection_id, sender)?;
+
+ let mut white_list_collection: Vec<T::AccountId>;
+ if <WhiteList<T>>::contains_key(collection_id) {
+ white_list_collection = <WhiteList<T>>::get(collection_id);
+ if !white_list_collection.contains(&address.clone())
+ {
+ white_list_collection.push(address.clone());
+ }
+ }
+ else {
+ white_list_collection = Vec::new();
+ white_list_collection.push(address.clone());
+ }
+
+ <WhiteList<T>>::insert(collection_id, white_list_collection);
+ Ok(())
+ }
+
+ #[weight = 0]
+ pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{
+
+ let sender = ensure_signed(origin)?;
+ Self::check_owner_or_admin_permissions(collection_id, sender)?;
+
+ if <WhiteList<T>>::contains_key(collection_id) {
+ let mut white_list_collection = <WhiteList<T>>::get(collection_id);
+ if white_list_collection.contains(&address.clone())
+ {
+ white_list_collection.retain(|i| *i != address.clone());
+ <WhiteList<T>>::insert(collection_id, white_list_collection);
+ }
+ }
+
+ Ok(())
+ }
+
+ #[weight = 0]
+ pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult
+ {
+ let sender = ensure_signed(origin)?;
+
+ Self::check_owner_permissions(collection_id, sender)?;
+ let mut target_collection = <Collection<T>>::get(collection_id);
+ target_collection.access = mode;
+ <Collection<T>>::insert(collection_id, target_collection);
+
+ Ok(())
+ }
+
+ #[weight = 0]
+ pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult
+ {
+ let sender = ensure_signed(origin)?;
+
+ Self::check_owner_permissions(collection_id, sender)?;
+ let mut target_collection = <Collection<T>>::get(collection_id);
+ target_collection.mint_mode = mint_permission;
+ <Collection<T>>::insert(collection_id, target_collection);
+
+ Ok(())
+ }
+
+ #[weight = 0]
pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
@@ -405,9 +471,17 @@
pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {
let sender = ensure_signed(origin)?;
+ Self::collection_exists(collection_id)?;
let target_collection = <Collection<T>>::get(collection_id);
- Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
+ if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+ if target_collection.mint_mode == false {
+ panic!("Collection is not in mint mode");
+ }
+
+ Self::check_white_list(collection_id, owner.clone())?;
+ }
+
match target_collection.mode
{
CollectionMode::NFT(_) => {
@@ -469,10 +543,13 @@
pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {
let sender = ensure_signed(origin)?;
+ Self::collection_exists(collection_id)?;
let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
if !item_owner
{
- Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;
+ if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+ Self::check_white_list(collection_id, sender.clone())?;
+ }
}
let target_collection = <Collection<T>>::get(collection_id);
@@ -494,11 +571,15 @@
pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {
let sender = ensure_signed(origin)?;
- ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");
+ let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
+ if !item_owner {
+ Self::check_white_list(collection_id, sender.clone())?;
+ Self::check_white_list(collection_id, recipient.clone())?;
+ }
+
let target_collection = <Collection<T>>::get(collection_id);
- // TODO: implement other modes
match target_collection.mode
{
CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,
@@ -518,7 +599,10 @@
// amount param stub
let amount = 100000000;
- ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id), "Only item owner can call transfer method");
+ let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);
+ if !item_owner {
+ Self::check_white_list(collection_id, approved.clone())?;
+ }
let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
if list_exists {
@@ -547,6 +631,9 @@
let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone()));
if approved_list_exists
{
+ Self::check_white_list(collection_id, from.clone())?;
+ Self::check_white_list(collection_id, recipient.clone())?;
+
let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));
let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
ensure!(opt_item.is_some(), "No approve found");
@@ -559,7 +646,7 @@
}
else
{
- Self::check_owner_or_admin_permissions(collection_id, sender)?;
+ panic!("Only approved addresses can call this method");
}
let target_collection = <Collection<T>>::get(collection_id);
@@ -675,11 +762,7 @@
Ok(())
}
- fn burn_refungible_item(
- collection_id: u64,
- item_id: u64,
- owner: T::AccountId,
- ) -> DispatchResult {
+ fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {
ensure!(
<ReFungibleItemList<T>>::contains_key(collection_id, item_id),
"Item does not exists"
@@ -770,26 +853,31 @@
Ok(())
}
- fn check_owner_or_admin_permissions(
- collection_id: u64,
- subject: T::AccountId,
- ) -> DispatchResult {
- Self::collection_exists(collection_id)?;
+ fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {
let target_collection = <Collection<T>>::get(collection_id);
- let is_owner = subject == target_collection.owner;
-
- let no_perm_mes = "You do not have permissions to modify this collection";
+ let mut result: bool = subject == target_collection.owner;
let exists = <AdminList<T>>::contains_key(collection_id);
- if !is_owner {
- ensure!(exists, no_perm_mes);
- ensure!(
- <AdminList<T>>::get(collection_id).contains(&subject),
- no_perm_mes
- );
+ if !result & exists {
+ if <AdminList<T>>::get(collection_id).contains(&subject) {
+ result = true
+ }
}
- Ok(())
+
+ result
+ }
+
+ fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {
+
+ Self::collection_exists(collection_id)?;
+ let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());
+
+ if result == true {
+ Ok(())
+ } else {
+ panic!("You do not have permissions to modify this collection")
+ }
}
fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {
@@ -812,6 +900,21 @@
}
}
+ fn check_white_list(collection_id: u64, address: T::AccountId) -> DispatchResult {
+
+ let mes = "Address is not in white list";
+ if <WhiteList<T>>::contains_key(collection_id){
+ let wl = <WhiteList<T>>::get(collection_id);
+ if !wl.contains(&address.clone()) {
+ panic!(mes);
+ }
+ }
+ else {
+ panic!(mes);
+ }
+ Ok(())
+ }
+
fn transfer_fungible(
collection_id: u64,
item_id: u64,
@@ -819,6 +922,12 @@
owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
+
+ ensure!(
+ <FungibleItemList<T>>::contains_key(collection_id, item_id),
+ "Item not exists"
+ );
+
let full_item = <FungibleItemList<T>>::get(collection_id, item_id);
let amount = full_item.value;
@@ -903,6 +1012,12 @@
owner: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
+
+ ensure!(
+ <ReFungibleItemList<T>>::contains_key(collection_id, item_id),
+ "Item not exists"
+ );
+
let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);
let item = full_item
.owner
@@ -983,6 +1098,12 @@
sender: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
+
+ ensure!(
+ <NftItemList<T>>::contains_key(collection_id, item_id),
+ "Item not exists"
+ );
+
let mut item = <NftItemList<T>>::get(collection_id, item_id);
ensure!(
pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,6 +1,6 @@
// Tests to be written here
use crate::mock::*;
-use crate::{ApprovePermissions, CollectionMode, Ownership};
+use crate::{ApprovePermissions, CollectionMode, AccessMode, Ownership};
use frame_support::{assert_noop, assert_ok};
#[test]
@@ -321,10 +321,11 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -390,10 +391,11 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -461,10 +463,11 @@
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
- assert_noop!(
- TemplateModule::transfer_from(origin2.clone(), 1, 3, 1, 1, 1),
- "You do not have permissions to modify this collection"
- );
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
@@ -573,7 +576,6 @@
let mode: CollectionMode = CollectionMode::NFT(2000);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
assert_ok!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
@@ -583,7 +585,7 @@
));
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
- origin2.clone(),
+ origin1.clone(),
1,
[1, 2, 3].to_vec(),
1
@@ -614,7 +616,6 @@
let mode: CollectionMode = CollectionMode::Fungible(3);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
assert_ok!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
@@ -624,7 +625,7 @@
));
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
- origin2.clone(),
+ origin1.clone(),
1,
[].to_vec(),
1
@@ -661,6 +662,11 @@
token_prefix1.clone(),
mode
));
+
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+
assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
assert_ok!(TemplateModule::create_item(
origin2.clone(),
@@ -928,6 +934,13 @@
// approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
+
+ assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), 1, true));
+ assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), 1, AccessMode::WhiteList));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
+
assert_ok!(TemplateModule::transfer_from(
origin2.clone(),
1,
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -111,7 +111,7 @@
spec_name: create_runtime_str!("nft"),
impl_name: create_runtime_str!("nft"),
authoring_version: 1,
- spec_version: 1,
+ spec_version: 2,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,