difftreelog
Merge pull request #16 from usetech-llc/feature/NFTPAR-142
in: master
Feature/nftpar-142
7 files changed
README.mddiffbeforeafterboth--- a/README.md
+++ b/README.md
@@ -167,9 +167,9 @@
"CollectionMode": {
"_enum": {
"Invalid": null,
- "NFT": "u32",
+ "NFT": null,
"Fungible": "u32",
- "ReFungible": "(u32, u32)"
+ "ReFungible": "u32"
}
},
"Ownership": {
@@ -189,7 +189,8 @@
"NftItemType": {
"Collection": "u64",
"Owner": "AccountId",
- "Data": "Vec<u8>"
+ "ConstData": "Vec<u8>",
+ "VariableData": "Vec<u8>"
},
"Ownership": {
"owner": "AccountId",
@@ -198,7 +199,8 @@
"ReFungibleItemType": {
"Collection": "u64",
"Owner": "Vec<Ownership<AccountId>>",
- "Data": "Vec<u8>"
+ "ConstData": "Vec<u8>",
+ "VariableData": "Vec<u8>"
},
"CollectionType": {
"Owner": "AccountId",
@@ -208,11 +210,12 @@
"Name": "Vec<u16>",
"Description": "Vec<u16>",
"TokenPrefix": "Vec<u8>",
- "CustomDataSize": "u32",
"MintMode": "bool",
"OffchainSchema": "Vec<u8>",
"Sponsor": "AccountId",
- "UnconfirmedSponsor": "AccountId"
+ "UnconfirmedSponsor": "AccountId",
+ "VariableOnChainSchema": "Vec<u8>",
+ "ConstOnChainSchema": "Vec<u8>"
},
"ApprovePermissions": {
"Approved": "AccountId",
@@ -221,7 +224,23 @@
"RawData": "Vec<u8>",
"Address": "AccountId",
"LookupSource": "AccountId",
- "Weight": "u64"
+ "Weight": "u64",
+ "CreateNftData": {
+ "const_data": "Vec<u8>",
+ "variable_data": "Vec<u8>"
+ },
+ "CreateFungibleData": {},
+ "CreateReFungibleData": {
+ "const_data": "Vec<u8>",
+ "variable_data": "Vec<u8>"
+ },
+ "CreateItemData": {
+ "_enum": {
+ "NFT": "CreateNftData",
+ "Fungible": "CreateFungibleData",
+ "ReFungible": "CreateReFungibleData"
+ }
+ }
}
```
\ No newline at end of file
node/src/chain_spec.rsdiffbeforeafterboth--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -159,17 +159,18 @@
1,
CollectionType {
owner: get_account_id_from_seed::<sr25519::Public>("Alice"),
- mode: CollectionMode::NFT(50),
+ mode: CollectionMode::NFT,
access: AccessMode::Normal,
decimal_points: 0,
name: vec![],
description: vec![],
token_prefix: vec![],
- custom_data_size: 50,
mint_mode: false,
offchain_schema: vec![],
sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
+ const_on_chain_schema: vec![],
+ variable_on_chain_schema: vec![]
},
)],
nft_item_id: vec![],
pallets/nft/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -9,6 +9,19 @@
const SEED: u32 = 1;
+ fn default_nft_data() -> CreateItemData {
+ CreateItemData::NFT(CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+ }
+
+ fn default_fungible_data () -> CreateItemData {
+ CreateItemData::Fungible(CreateFungibleData { })
+ }
+
+ fn default_re_fungible_data () -> CreateItemData {
+ CreateItemData::ReFungible(CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+ }
+
+
benchmarks! {
_ {}
@@ -17,7 +30,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = account("caller", 0, SEED);
}: create_collection(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)
verify {
@@ -28,7 +41,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
}: destroy_collection(RawOrigin::Signed(caller.clone()), 2)
@@ -37,7 +50,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
let whitelist_account: T::AccountId = account("admin", 0, SEED);
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
@@ -47,7 +60,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
let whitelist_account: T::AccountId = account("admin", 0, SEED);
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
@@ -58,7 +71,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
}: set_public_access_mode(RawOrigin::Signed(caller.clone()), 2, AccessMode::WhiteList)
@@ -67,7 +80,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
}: set_mint_permission(RawOrigin::Signed(caller.clone()), 2, true)
@@ -76,7 +89,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let new_owner: T::AccountId = account("admin", 0, SEED);
@@ -86,7 +99,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let new_admin: T::AccountId = account("admin", 0, SEED);
@@ -96,7 +109,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let new_admin: T::AccountId = account("admin", 0, SEED);
@@ -107,7 +120,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
}: set_collection_sponsor(RawOrigin::Signed(caller.clone()), 2, caller.clone())
@@ -116,7 +129,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
@@ -126,7 +139,7 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
@@ -138,26 +151,32 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
-
- }: create_item(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec(), caller.clone())
+ let data = default_nft_data();
+
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
#[extra]
create_item_nft_large {
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- let mut item_large: Vec<u8> = Vec::new();
+ let nft_data = CreateNftData {
+ const_data: vec![],
+ variable_data: vec![]
+ };
for i in 0..1998 {
- item_large.push(10);
+ nft_data.const_data.push(10);
+ nft_data.variable_data.push(10);
}
+ let mut data = CreateItemData::NFT(nft_data);
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: create_item(RawOrigin::Signed(caller.clone()), 2, item_large, caller.clone())
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
// fungible item
create_item_fungible {
@@ -167,28 +186,31 @@
let mode: CollectionMode = CollectionMode::Fungible(3);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let data = default_fungible_data();
- }: create_item(RawOrigin::Signed(caller.clone()), 2, [].to_vec(), caller.clone())
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
// refungible item
create_item_refungible {
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3, 3);
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let data = default_re_fungible_data();
- }: create_item(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec(), caller.clone())
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
burn_item {
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1, 2, 3].to_vec(), caller.clone())?;
+ let data = default_nft_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
}: burn_item(RawOrigin::Signed(caller.clone()), 2, 1)
@@ -196,11 +218,12 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ let mode: CollectionMode = CollectionMode::NFT;
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1, 2, 3].to_vec(), caller.clone())?;
+ let data = default_nft_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
@@ -212,7 +235,8 @@
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [].to_vec(), caller.clone())?;
+ let data = default_fungible_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
@@ -220,11 +244,12 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3,3);
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ let data = default_re_fungible_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
}: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
@@ -232,11 +257,12 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3,3);
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ let data = default_re_fungible_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
}: approve(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1)
@@ -245,11 +271,12 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(300);
+ let mode: CollectionMode = CollectionMode::NFT;
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ let data = default_nft_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
@@ -263,7 +290,8 @@
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [].to_vec(), caller.clone())?;
+ let data = default_fungible_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
@@ -273,11 +301,12 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3,3);
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
let recipient: T::AccountId = account("recipient", 0, SEED);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ let data = default_re_fungible_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
}: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
@@ -286,9 +315,39 @@
let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3,3);
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
}: set_offchain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
- }
\ No newline at end of file
+
+ set_const_on_chain_schema {
+ let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
+ let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ }: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+
+ set_variable_on_chain_schema {
+ let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+ let mode: CollectionMode = CollectionMode::ReFungible(3);
+ let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ }: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+
+ set_variable_meta_data {
+ let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+ let mode: CollectionMode = CollectionMode::NFT;
+ let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let data = default_nft_data();
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+
+ }: set_variable_meta_data(RawOrigin::Signed(caller.clone()), 2, 1, [1, 2, 3].to_vec())
+}
\ No newline at end of file
pallets/nft/src/default_weights.rsdiffbeforeafterboth--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -92,6 +92,21 @@
.saturating_add(DbWeight::get().reads(2 as Weight))
.saturating_add(DbWeight::get().writes(1 as Weight))
}
+ fn set_const_on_chain_schema() -> Weight {
+ (11_100_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_on_chain_schema() -> Weight {
+ (11_100_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_meta_data() -> Weight {
+ (17_500_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
// fn enable_contract_sponsoring() -> Weight {
// (0 as Weight)
// .saturating_add(DbWeight::get().reads(1 as Weight))
pallets/nft/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage,12 dispatch::DispatchResult,13 ensure, parameter_types,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30 traits::{31 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,32 },33 transaction_validity::{34 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35 },36 FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55 Invalid,56 // custom data size57 NFT(u32),58 // decimal points59 Fungible(u32),60 // custom data size and decimal points61 ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65 fn into(self) -> u8 {66 match self {67 CollectionMode::Invalid => 0,68 CollectionMode::NFT(_) => 1,69 CollectionMode::Fungible(_) => 2,70 CollectionMode::ReFungible(_, _) => 3,71 }72 }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78 Normal,79 WhiteList,80}81impl Default for AccessMode {82 fn default() -> Self {83 Self::Normal84 }85}8687impl Default for CollectionMode {88 fn default() -> Self {89 Self::Invalid90 }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96 pub owner: AccountId,97 pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103 pub owner: AccountId,104 pub mode: CollectionMode,105 pub access: AccessMode,106 pub decimal_points: u32,107 pub name: Vec<u16>, // 64 include null escape char108 pub description: Vec<u16>, // 256 include null escape char109 pub token_prefix: Vec<u8>, // 16 include null escape char110 pub custom_data_size: u32,111 pub mint_mode: bool,112 pub offchain_schema: Vec<u8>,113 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender114 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120 pub admin: AccountId,121 pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127 pub collection: u64,128 pub owner: AccountId,129 pub data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135 pub collection: u64,136 pub owner: AccountId,137 pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143 pub collection: u64,144 pub owner: Vec<Ownership<AccountId>>,145 pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151 pub approved: AccountId,152 pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158 pub sender: AccountId,159 pub recipient: AccountId,160 pub collection_id: u64,161 pub item_id: u64,162 pub amount: u64,163 pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169 pub address: AccountId,170 pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176 pub collection_numbers_limit: u64,177 pub account_token_ownership_limit: u64,178 pub collections_admins_limit: u64,179 pub custom_data_limit: u32,180181 // Timeouts for item types in passed blocks182 pub nft_sponsor_transfer_timeout: u32,183 pub fungible_sponsor_transfer_timeout: u32,184 pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188 fn create_collection() -> Weight;189 fn destroy_collection() -> Weight;190 fn add_to_white_list() -> Weight;191 fn remove_from_white_list() -> Weight;192 fn set_public_access_mode() -> Weight;193 fn set_mint_permission() -> Weight;194 fn change_collection_owner() -> Weight;195 fn add_collection_admin() -> Weight;196 fn remove_collection_admin() -> Weight;197 fn set_collection_sponsor() -> Weight;198 fn confirm_sponsorship() -> Weight;199 fn remove_collection_sponsor() -> Weight;200 fn create_item(s: usize, ) -> Weight;201 fn burn_item() -> Weight;202 fn transfer() -> Weight;203 fn approve() -> Weight;204 fn transfer_from() -> Weight;205 fn set_offchain_schema() -> Weight;206 // fn enable_contract_sponsoring() -> Weight;207}208209pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {210 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;211212 /// Weight information for extrinsics in this pallet.213 type WeightInfo: WeightInfo;214}215216#[cfg(feature = "runtime-benchmarks")]217mod benchmarking;218219// #endregion220221decl_storage! {222 trait Store for Module<T: Trait> as Nft {223224 // Private members225 NextCollectionID: u64;226 CreatedCollectionCount: u64;227 ChainVersion: u64;228 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;229230 // Chain limits struct231 pub ChainLimit get(fn chain_limit) config(): ChainLimits;232233 // Bound counters234 CollectionCount: u64;235 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;236237 // Basic collections238 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;239 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;240 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;241242 /// Balance owner per collection map243 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;244245 /// second parameter: item id + owner account id246 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;247248 /// Item collections249 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;250 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;251 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;252253 /// Index list254 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;255256 /// Tokens transfer baskets257 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;258 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;259 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;260261 // Contract Sponsorship and Ownership262 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;263 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;264 }265 add_extra_genesis {266 build(|config: &GenesisConfig<T>| {267 // Modification of storage268 for (_num, _c) in &config.collection {269 <Module<T>>::init_collection(_c);270 }271272 for (_num, _q, _i) in &config.nft_item_id {273 <Module<T>>::init_nft_token(_i);274 }275276 for (_num, _q, _i) in &config.fungible_item_id {277 <Module<T>>::init_fungible_token(_i);278 }279280 for (_num, _q, _i) in &config.refungible_item_id {281 <Module<T>>::init_refungible_token(_i);282 }283 })284 }285}286287decl_event!(288 pub enum Event<T>289 where290 AccountId = <T as system::Trait>::AccountId,291 {292 /// New collection was created293 /// 294 /// # Arguments295 /// 296 /// * collection_id: Globally unique identifier of newly created collection.297 /// 298 /// * mode: [CollectionMode] converted into u8.299 /// 300 /// * account_id: Collection owner.301 Created(u64, u8, AccountId),302303 /// New item was created.304 /// 305 /// # Arguments306 /// 307 /// * collection_id: Id of the collection where item was created.308 /// 309 /// * item_id: Id of an item. Unique within the collection.310 ItemCreated(u64, u64),311312 /// Collection item was burned.313 /// 314 /// # Arguments315 /// 316 /// collection_id.317 /// 318 /// item_id: Identifier of burned NFT.319 ItemDestroyed(u64, u64),320 }321);322323decl_module! {324 pub struct Module<T: Trait> for enum Call where origin: T::Origin {325326 fn deposit_event() = default;327328 fn on_initialize(now: T::BlockNumber) -> Weight {329330 if ChainVersion::get() < 2331 {332 let value = NextCollectionID::get();333 CreatedCollectionCount::put(value);334 ChainVersion::put(2);335 }336337 0338 }339340 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.341 /// 342 /// # Permissions343 /// 344 /// * Anyone.345 /// 346 /// # Arguments347 /// 348 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.349 /// 350 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.351 /// 352 /// * token_prefix: UTF-8 string with token prefix.353 /// 354 /// * mode: [CollectionMode] collection type and type dependent data.355 // returns collection ID356 #[weight = T::WeightInfo::create_collection()]357 pub fn create_collection(origin,358 collection_name: Vec<u16>,359 collection_description: Vec<u16>,360 token_prefix: Vec<u8>,361 mode: CollectionMode) -> DispatchResult {362363 // Anyone can create a collection364 let who = ensure_signed(origin)?;365 let custom_data_size = match mode {366 CollectionMode::NFT(size) => {367368 // bound Custom data size369 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");370 size371 },372 CollectionMode::ReFungible(size, _) => {373374 // bound Custom data size375 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");376 size377 },378 _ => 0379 };380381 let decimal_points = match mode {382 CollectionMode::Fungible(points) => points,383 CollectionMode::ReFungible(_, points) => points,384 _ => 0385 };386387 // bound Total number of collections388 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");389390 // check params391 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");392393 let mut name = collection_name.to_vec();394 name.push(0);395 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");396397 let mut description = collection_description.to_vec();398 description.push(0);399 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");400401 let mut prefix = token_prefix.to_vec();402 prefix.push(0);403 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");404405 // Generate next collection ID406 let next_id = CreatedCollectionCount::get()407 .checked_add(1)408 .expect("collection id error");409410 // bound counter411 let total = CollectionCount::get()412 .checked_add(1)413 .expect("collection counter error");414415 CreatedCollectionCount::put(next_id);416 CollectionCount::put(total);417418 // Create new collection419 let new_collection = CollectionType {420 owner: who.clone(),421 name: name,422 mode: mode.clone(),423 mint_mode: false,424 access: AccessMode::Normal,425 description: description,426 decimal_points: decimal_points,427 token_prefix: prefix,428 offchain_schema: Vec::new(),429 custom_data_size: custom_data_size,430 sponsor: T::AccountId::default(),431 unconfirmed_sponsor: T::AccountId::default(),432 };433434 // Add new collection to map435 <Collection<T>>::insert(next_id, new_collection);436437 // call event438 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));439440 Ok(())441 }442443 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.444 /// 445 /// # Permissions446 /// 447 /// * Collection Owner.448 /// 449 /// # Arguments450 /// 451 /// * collection_id: collection to destroy.452 #[weight = T::WeightInfo::destroy_collection()]453 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {454455 let sender = ensure_signed(origin)?;456 Self::check_owner_permissions(collection_id, sender)?;457458 <AddressTokens<T>>::remove_prefix(collection_id);459 <ApprovedList<T>>::remove_prefix(collection_id);460 <Balance<T>>::remove_prefix(collection_id);461 <ItemListIndex>::remove(collection_id);462 <AdminList<T>>::remove(collection_id);463 <Collection<T>>::remove(collection_id);464 <WhiteList<T>>::remove(collection_id);465466 <NftItemList<T>>::remove_prefix(collection_id);467 <FungibleItemList<T>>::remove_prefix(collection_id);468 <ReFungibleItemList<T>>::remove_prefix(collection_id);469470 <NftTransferBasket<T>>::remove_prefix(collection_id);471 <FungibleTransferBasket<T>>::remove_prefix(collection_id);472 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);473474 if CollectionCount::get() > 0475 {476 // bound couter477 let total = CollectionCount::get()478 .checked_sub(1)479 .expect("collection counter error");480481 CollectionCount::put(total);482 }483484 Ok(())485 }486487 /// Add an address to white list.488 /// 489 /// # Permissions490 /// 491 /// * Collection Owner492 /// * Collection Admin493 /// 494 /// # Arguments495 /// 496 /// * collection_id.497 /// 498 /// * address.499 #[weight = T::WeightInfo::add_to_white_list()]500 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{501502 let sender = ensure_signed(origin)?;503 Self::check_owner_or_admin_permissions(collection_id, sender)?;504505 let mut white_list_collection: Vec<T::AccountId>;506 if <WhiteList<T>>::contains_key(collection_id) {507 white_list_collection = <WhiteList<T>>::get(collection_id);508 if !white_list_collection.contains(&address.clone())509 {510 white_list_collection.push(address.clone());511 }512 }513 else {514 white_list_collection = Vec::new();515 white_list_collection.push(address.clone());516 }517518 <WhiteList<T>>::insert(collection_id, white_list_collection);519 Ok(())520 }521522 /// Remove an address from white list.523 /// 524 /// # Permissions525 /// 526 /// * Collection Owner527 /// * Collection Admin528 /// 529 /// # Arguments530 /// 531 /// * collection_id.532 /// 533 /// * address.534 #[weight = T::WeightInfo::remove_from_white_list()]535 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{536537 let sender = ensure_signed(origin)?;538 Self::check_owner_or_admin_permissions(collection_id, sender)?;539540 if <WhiteList<T>>::contains_key(collection_id) {541 let mut white_list_collection = <WhiteList<T>>::get(collection_id);542 if white_list_collection.contains(&address.clone())543 {544 white_list_collection.retain(|i| *i != address.clone());545 <WhiteList<T>>::insert(collection_id, white_list_collection);546 }547 }548549 Ok(())550 }551552 /// Toggle between normal and white list access for the methods with access for `Anyone`.553 /// 554 /// # Permissions555 /// 556 /// * Collection Owner.557 /// 558 /// # Arguments559 /// 560 /// * collection_id.561 /// 562 /// * mode: [AccessMode]563 #[weight = T::WeightInfo::set_public_access_mode()]564 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult565 {566 let sender = ensure_signed(origin)?;567568 Self::check_owner_permissions(collection_id, sender)?;569 let mut target_collection = <Collection<T>>::get(collection_id);570 target_collection.access = mode;571 <Collection<T>>::insert(collection_id, target_collection);572573 Ok(())574 }575576 /// Allows Anyone to create tokens if:577 /// * White List is enabled, and578 /// * Address is added to white list, and579 /// * This method was called with True parameter580 /// 581 /// # Permissions582 /// * Collection Owner583 ///584 /// # Arguments585 /// 586 /// * collection_id.587 /// 588 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.589 #[weight = T::WeightInfo::set_mint_permission()]590 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult591 {592 let sender = ensure_signed(origin)?;593594 Self::check_owner_permissions(collection_id, sender)?;595 let mut target_collection = <Collection<T>>::get(collection_id);596 target_collection.mint_mode = mint_permission;597 <Collection<T>>::insert(collection_id, target_collection);598599 Ok(())600 }601602 /// Change the owner of the collection.603 /// 604 /// # Permissions605 /// 606 /// * Collection Owner.607 /// 608 /// # Arguments609 /// 610 /// * collection_id.611 /// 612 /// * new_owner.613 #[weight = T::WeightInfo::change_collection_owner()]614 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {615616 let sender = ensure_signed(origin)?;617 Self::check_owner_permissions(collection_id, sender)?;618 let mut target_collection = <Collection<T>>::get(collection_id);619 target_collection.owner = new_owner;620 <Collection<T>>::insert(collection_id, target_collection);621622 Ok(())623 }624625 /// Adds an admin of the Collection.626 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 627 /// 628 /// # Permissions629 /// 630 /// * Collection Owner.631 /// * Collection Admin.632 /// 633 /// # Arguments634 /// 635 /// * collection_id: ID of the Collection to add admin for.636 /// 637 /// * new_admin_id: Address of new admin to add.638 #[weight = T::WeightInfo::add_collection_admin()]639 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {640641 let sender = ensure_signed(origin)?;642 Self::check_owner_or_admin_permissions(collection_id, sender)?;643 let mut admin_arr: Vec<T::AccountId> = Vec::new();644645 if <AdminList<T>>::contains_key(collection_id)646 {647 admin_arr = <AdminList<T>>::get(collection_id);648 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");649 }650651 // Number of collection admins652 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");653654 admin_arr.push(new_admin_id);655 <AdminList<T>>::insert(collection_id, admin_arr);656657 Ok(())658 }659660 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.661 ///662 /// # Permissions663 /// 664 /// * Collection Owner.665 /// * Collection Admin.666 /// 667 /// # Arguments668 /// 669 /// * collection_id: ID of the Collection to remove admin for.670 /// 671 /// * account_id: Address of admin to remove.672 #[weight = T::WeightInfo::remove_collection_admin()]673 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {674675 let sender = ensure_signed(origin)?;676 Self::check_owner_or_admin_permissions(collection_id, sender)?;677678 if <AdminList<T>>::contains_key(collection_id)679 {680 let mut admin_arr = <AdminList<T>>::get(collection_id);681 admin_arr.retain(|i| *i != account_id);682 <AdminList<T>>::insert(collection_id, admin_arr);683 }684685 Ok(())686 }687688 /// # Permissions689 /// 690 /// * Collection Owner691 /// 692 /// # Arguments693 /// 694 /// * collection_id.695 /// 696 /// * new_sponsor.697 #[weight = T::WeightInfo::set_collection_sponsor()]698 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {699700 let sender = ensure_signed(origin)?;701 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");702703 let mut target_collection = <Collection<T>>::get(collection_id);704 ensure!(sender == target_collection.owner, "You do not own this collection");705706 target_collection.unconfirmed_sponsor = new_sponsor;707 <Collection<T>>::insert(collection_id, target_collection);708709 Ok(())710 }711712 /// # Permissions713 /// 714 /// * Sponsor.715 /// 716 /// # Arguments717 /// 718 /// * collection_id.719 #[weight = T::WeightInfo::confirm_sponsorship()]720 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {721722 let sender = ensure_signed(origin)?;723 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");724725 let mut target_collection = <Collection<T>>::get(collection_id);726 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");727728 target_collection.sponsor = target_collection.unconfirmed_sponsor;729 target_collection.unconfirmed_sponsor = T::AccountId::default();730 <Collection<T>>::insert(collection_id, target_collection);731732 Ok(())733 }734735 /// Switch back to pay-per-own-transaction model.736 ///737 /// # Permissions738 ///739 /// * Collection owner.740 /// 741 /// # Arguments742 /// 743 /// * collection_id.744 #[weight = T::WeightInfo::remove_collection_sponsor()]745 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {746747 let sender = ensure_signed(origin)?;748 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");749750 let mut target_collection = <Collection<T>>::get(collection_id);751 ensure!(sender == target_collection.owner, "You do not own this collection");752753 target_collection.sponsor = T::AccountId::default();754 <Collection<T>>::insert(collection_id, target_collection);755756 Ok(())757 }758759 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.760 /// 761 /// # Permissions762 /// 763 /// * Collection Owner.764 /// * Collection Admin.765 /// * Anyone if766 /// * White List is enabled, and767 /// * Address is added to white list, and768 /// * MintPermission is enabled (see SetMintPermission method)769 /// 770 /// # Arguments771 /// 772 /// * collection_id: ID of the collection.773 /// 774 /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.775 /// 776 /// * owner: Address, initial owner of the NFT.777 // #[weight =778 // (130_000_000 as Weight)779 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))780 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))781 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]782783 #[weight = T::WeightInfo::create_item(properties.len())]784 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {785786 let sender = ensure_signed(origin)?;787 Self::collection_exists(collection_id)?;788 let target_collection = <Collection<T>>::get(collection_id);789790 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {791 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");792 Self::check_white_list(collection_id, &owner)?;793 Self::check_white_list(collection_id, &sender)?;794 }795796 match target_collection.mode797 {798 CollectionMode::NFT(_) => {799800 // check size801 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");802803 // Create nft item804 let item = NftItemType {805 collection: collection_id,806 owner: owner,807 data: properties.clone(),808 };809810 Self::add_nft_item(item)?;811812 },813 CollectionMode::Fungible(_) => {814815 // check size816 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");817818 let item = FungibleItemType {819 collection: collection_id,820 owner: owner,821 value: (10 as u128).pow(target_collection.decimal_points)822 };823824 Self::add_fungible_item(item)?;825 },826 CollectionMode::ReFungible(_, _) => {827828 // check size829 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");830831 let mut owner_list = Vec::new();832 let value = (10 as u128).pow(target_collection.decimal_points);833 owner_list.push(Ownership {owner: owner.clone(), fraction: value});834835 let item = ReFungibleItemType {836 collection: collection_id,837 owner: owner_list,838 data: properties.clone()839 };840841 Self::add_refungible_item(item)?;842 },843 _ => { ensure!(1 == 0,"just error"); }844845 };846847 // call event848 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));849850 Ok(())851 }852853 /// Destroys a concrete instance of NFT.854 /// 855 /// # Permissions856 /// 857 /// * Collection Owner.858 /// * Collection Admin.859 /// * Current NFT Owner.860 /// 861 /// # Arguments862 /// 863 /// * collection_id: ID of the collection.864 /// 865 /// * item_id: ID of NFT to burn.866 #[weight = T::WeightInfo::burn_item()]867 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {868869 let sender = ensure_signed(origin)?;870 Self::collection_exists(collection_id)?;871872 // Transfer permissions check873 let target_collection = <Collection<T>>::get(collection_id);874 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||875 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),876 "Only item owner, collection owner and admins can modify item");877878 if target_collection.access == AccessMode::WhiteList {879 Self::check_white_list(collection_id, &sender)?;880 }881882 match target_collection.mode883 {884 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,885 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,886 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,887 _ => ()888 };889890 // call event891 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));892893 Ok(())894 }895896 /// Change ownership of the token.897 /// 898 /// # Permissions899 /// 900 /// * Collection Owner901 /// * Collection Admin902 /// * Current NFT owner903 ///904 /// # Arguments905 /// 906 /// * recipient: Address of token recipient.907 /// 908 /// * collection_id.909 /// 910 /// * item_id: ID of the item911 /// * Non-Fungible Mode: Required.912 /// * Fungible Mode: Ignored.913 /// * Re-Fungible Mode: Required.914 /// 915 /// * value: Amount to transfer.916 /// * Non-Fungible Mode: Ignored917 /// * Fungible Mode: Must specify transferred amount918 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)919 #[weight = T::WeightInfo::transfer()]920 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {921922 let sender = ensure_signed(origin)?;923924 // Transfer permissions check925 let target_collection = <Collection<T>>::get(collection_id);926 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||927 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),928 "Only item owner, collection owner and admins can modify item");929930 if target_collection.access == AccessMode::WhiteList {931 Self::check_white_list(collection_id, &sender)?;932 Self::check_white_list(collection_id, &recipient)?;933 }934935 match target_collection.mode936 {937 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,938 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,939 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,940 _ => ()941 };942943 Ok(())944 }945946 /// Set, change, or remove approved address to transfer the ownership of the NFT.947 /// 948 /// # Permissions949 /// 950 /// * Collection Owner951 /// * Collection Admin952 /// * Current NFT owner953 /// 954 /// # Arguments955 /// 956 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).957 /// 958 /// * collection_id.959 /// 960 /// * item_id: ID of the item.961 #[weight = T::WeightInfo::approve()]962 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {963964 let sender = ensure_signed(origin)?;965966 // Transfer permissions check967 let target_collection = <Collection<T>>::get(collection_id);968 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||969 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),970 "Only item owner, collection owner and admins can approve");971972 if target_collection.access == AccessMode::WhiteList {973 Self::check_white_list(collection_id, &sender)?;974 Self::check_white_list(collection_id, &approved)?;975 }976977 // amount param stub978 let amount = 100000000;979980 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));981 if list_exists {982983 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));984 let item_contains = list.iter().any(|i| i.approved == approved);985986 if !item_contains {987 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });988 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);989 }990 } else {991992 let mut list = Vec::new();993 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });994 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);995 }996997 Ok(())998 }999 1000 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1001 /// 1002 /// # Permissions1003 /// * Collection Owner1004 /// * Collection Admin1005 /// * Current NFT owner1006 /// * Address approved by current NFT owner1007 /// 1008 /// # Arguments1009 /// 1010 /// * from: Address that owns token.1011 /// 1012 /// * recipient: Address of token recipient.1013 /// 1014 /// * collection_id.1015 /// 1016 /// * item_id: ID of the item.1017 /// 1018 /// * value: Amount to transfer.1019 #[weight = T::WeightInfo::transfer_from()]1020 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10211022 let sender = ensure_signed(origin)?;1023 let mut appoved_transfer = false;10241025 // Check approve1026 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1027 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1028 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1029 if opt_item.is_some()1030 {1031 appoved_transfer = true;1032 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1033 }1034 }10351036 // Transfer permissions check1037 let target_collection = <Collection<T>>::get(collection_id);1038 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1039 "Only item owner, collection owner and admins can modify items");10401041 if target_collection.access == AccessMode::WhiteList {1042 Self::check_white_list(collection_id, &sender)?;1043 Self::check_white_list(collection_id, &recipient)?;1044 }10451046 // remove approve1047 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1048 .into_iter().filter(|i| i.approved != sender.clone()).collect();1049 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);105010511052 match target_collection.mode1053 {1054 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1055 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1056 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1057 _ => ()1058 };10591060 Ok(())1061 }10621063 ///1064 #[weight = 0]1065 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10661067 // let no_perm_mes = "You do not have permissions to modify this collection";1068 // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1069 // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1070 // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10711072 // // on_nft_received call10731074 // Self::transfer(origin, collection_id, item_id, new_owner)?;10751076 Ok(())1077 }10781079 /// Set off-chain data schema.1080 /// 1081 /// # Permissions1082 /// 1083 /// * Collection Owner1084 /// * Collection Admin1085 /// 1086 /// # Arguments1087 /// 1088 /// * collection_id.1089 /// 1090 /// * schema: String representing the offchain data schema.1091 #[weight = T::WeightInfo::set_offchain_schema()]1092 pub fn set_offchain_schema(1093 origin,1094 collection_id: u64,1095 schema: Vec<u8>1096 ) -> DispatchResult {1097 let sender = ensure_signed(origin)?;1098 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10991100 let mut target_collection = <Collection<T>>::get(collection_id);1101 target_collection.offchain_schema = schema;1102 <Collection<T>>::insert(collection_id, target_collection);11031104 Ok(())1105 }11061107 // Sudo permissions function1108 #[weight = 0]1109 pub fn set_chain_limits(1110 origin,1111 limits: ChainLimits1112 ) -> DispatchResult {1113 ensure_root(origin)?;1114 <ChainLimit>::put(limits);1115 Ok(())1116 }11171118 /// Enable smart contract self-sponsoring.1119 /// 1120 /// # Permissions1121 /// 1122 /// * Contract Owner1123 /// 1124 /// # Arguments1125 /// 1126 /// * contract address1127 /// * enable flag1128 /// 1129 #[weight = 0]1130 pub fn enable_contract_sponsoring(1131 origin,1132 contract_address: T::AccountId,1133 enable: bool1134 ) -> DispatchResult {1135 let sender = ensure_signed(origin)?;1136 let mut is_owner = false;1137 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1138 let owner = <ContractOwner<T>>::get(&contract_address);1139 is_owner = sender == owner;1140 }1141 ensure!(is_owner, "Only contract owner may call this method");11421143 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1144 Ok(())1145 }11461147 }1148}11491150impl<T: Trait> Module<T> {1151 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1152 let current_index = <ItemListIndex>::get(item.collection)1153 .checked_add(1)1154 .expect("Item list index id error");1155 let itemcopy = item.clone();1156 let owner = item.owner.clone();1157 let value = item.value as u64;11581159 Self::add_token_index(item.collection, current_index, owner.clone())?;11601161 <ItemListIndex>::insert(item.collection, current_index);1162 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11631164 // Add current block1165 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1166 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1167 1168 // Update balance1169 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1170 .checked_add(value)1171 .unwrap();1172 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11731174 Ok(())1175 }11761177 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1178 let current_index = <ItemListIndex>::get(item.collection)1179 .checked_add(1)1180 .expect("Item list index id error");1181 let itemcopy = item.clone();11821183 let value = item.owner.first().unwrap().fraction as u64;1184 let owner = item.owner.first().unwrap().owner.clone();11851186 Self::add_token_index(item.collection, current_index, owner.clone())?;11871188 <ItemListIndex>::insert(item.collection, current_index);1189 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11901191 // Add current block1192 let block_number: T::BlockNumber = 0.into();1193 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11941195 // Update balance1196 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1197 .checked_add(value)1198 .unwrap();1199 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12001201 Ok(())1202 }12031204 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1205 let current_index = <ItemListIndex>::get(item.collection)1206 .checked_add(1)1207 .expect("Item list index id error");12081209 let item_owner = item.owner.clone();1210 let collection_id = item.collection.clone();1211 Self::add_token_index(collection_id, current_index, item.owner.clone())?;12121213 <ItemListIndex>::insert(collection_id, current_index);1214 <NftItemList<T>>::insert(collection_id, current_index, item);12151216 // Add current block1217 let block_number: T::BlockNumber = 0.into();1218 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12191220 // Update balance1221 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1222 .checked_add(1)1223 .unwrap();1224 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12251226 Ok(())1227 }12281229 fn burn_refungible_item(1230 collection_id: u64,1231 item_id: u64,1232 owner: T::AccountId,1233 ) -> DispatchResult {1234 ensure!(1235 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1236 "Item does not exists"1237 );1238 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1239 let item = collection1240 .owner1241 .iter()1242 .filter(|&i| i.owner == owner)1243 .next()1244 .unwrap();1245 Self::remove_token_index(collection_id, item_id, owner.clone())?;12461247 // remove approve list1248 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12491250 // update balance1251 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1252 .checked_sub(item.fraction as u64)1253 .unwrap();1254 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12551256 <ReFungibleItemList<T>>::remove(collection_id, item_id);12571258 Ok(())1259 }12601261 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1262 ensure!(1263 <NftItemList<T>>::contains_key(collection_id, item_id),1264 "Item does not exists"1265 );1266 let item = <NftItemList<T>>::get(collection_id, item_id);1267 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12681269 // remove approve list1270 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12711272 // update balance1273 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1274 .checked_sub(1)1275 .unwrap();1276 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1277 <NftItemList<T>>::remove(collection_id, item_id);12781279 Ok(())1280 }12811282 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1283 ensure!(1284 <FungibleItemList<T>>::contains_key(collection_id, item_id),1285 "Item does not exists"1286 );1287 let item = <FungibleItemList<T>>::get(collection_id, item_id);1288 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12891290 // remove approve list1291 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12921293 // update balance1294 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1295 .checked_sub(item.value as u64)1296 .unwrap();1297 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12981299 <FungibleItemList<T>>::remove(collection_id, item_id);13001301 Ok(())1302 }13031304 fn collection_exists(collection_id: u64) -> DispatchResult {1305 ensure!(1306 <Collection<T>>::contains_key(collection_id),1307 "This collection does not exist"1308 );1309 Ok(())1310 }13111312 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1313 Self::collection_exists(collection_id)?;13141315 let target_collection = <Collection<T>>::get(collection_id);1316 ensure!(1317 subject == target_collection.owner,1318 "You do not own this collection"1319 );13201321 Ok(())1322 }13231324 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1325 let target_collection = <Collection<T>>::get(collection_id);1326 let mut result: bool = subject == target_collection.owner;1327 let exists = <AdminList<T>>::contains_key(collection_id);13281329 if !result & exists {1330 if <AdminList<T>>::get(collection_id).contains(&subject) {1331 result = true1332 }1333 }13341335 result1336 }13371338 fn check_owner_or_admin_permissions(1339 collection_id: u64,1340 subject: T::AccountId,1341 ) -> DispatchResult {1342 Self::collection_exists(collection_id)?;1343 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13441345 ensure!(1346 result,1347 "You do not have permissions to modify this collection"1348 );1349 Ok(())1350 }13511352 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1353 let target_collection = <Collection<T>>::get(collection_id);13541355 match target_collection.mode {1356 CollectionMode::NFT(_) => {1357 <NftItemList<T>>::get(collection_id, item_id).owner == subject1358 }1359 CollectionMode::Fungible(_) => {1360 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1361 }1362 CollectionMode::ReFungible(_, _) => {1363 <ReFungibleItemList<T>>::get(collection_id, item_id)1364 .owner1365 .iter()1366 .any(|i| i.owner == subject)1367 }1368 CollectionMode::Invalid => false,1369 }1370 }13711372 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1373 let mes = "Address is not in white list";1374 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1375 let wl = <WhiteList<T>>::get(collection_id);1376 ensure!(wl.contains(address), mes);13771378 Ok(())1379 }13801381 fn transfer_fungible(1382 collection_id: u64,1383 item_id: u64,1384 value: u64,1385 owner: T::AccountId,1386 new_owner: T::AccountId,1387 ) -> DispatchResult {1388 ensure!(1389 <FungibleItemList<T>>::contains_key(collection_id, item_id),1390 "Item not exists"1391 );13921393 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1394 let amount = full_item.value;13951396 ensure!(amount >= value.into(), "Item balance not enouth");13971398 // update balance1399 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1400 .checked_sub(value)1401 .unwrap();1402 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);14031404 let mut new_owner_account_id = 0;1405 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1406 if new_owner_items.len() > 0 {1407 new_owner_account_id = new_owner_items[0];1408 }14091410 let val64 = value.into();14111412 // transfer1413 if amount == val64 && new_owner_account_id == 0 {1414 // change owner1415 // new owner do not have account1416 let mut new_full_item = full_item.clone();1417 new_full_item.owner = new_owner.clone();1418 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14191420 // update balance1421 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1422 .checked_add(value)1423 .unwrap();1424 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14251426 // update index collection1427 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1428 } else {1429 let mut new_full_item = full_item.clone();1430 new_full_item.value -= val64;14311432 // separate amount1433 if new_owner_account_id > 0 {1434 // new owner has account1435 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1436 item.value += val64;14371438 // update balance1439 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1440 .checked_add(value)1441 .unwrap();1442 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14431444 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1445 } else {1446 // new owner do not have account1447 let item = FungibleItemType {1448 collection: collection_id,1449 owner: new_owner.clone(),1450 value: val64,1451 };14521453 Self::add_fungible_item(item)?;1454 }14551456 if amount == val64 {1457 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14581459 // remove approve list1460 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1461 <FungibleItemList<T>>::remove(collection_id, item_id);1462 }14631464 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1465 }14661467 Ok(())1468 }14691470 fn transfer_refungible(1471 collection_id: u64,1472 item_id: u64,1473 value: u64,1474 owner: T::AccountId,1475 new_owner: T::AccountId,1476 ) -> DispatchResult {1477 ensure!(1478 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1479 "Item not exists"1480 );14811482 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1483 let item = full_item1484 .owner1485 .iter()1486 .filter(|i| i.owner == owner)1487 .next()1488 .unwrap();1489 let amount = item.fraction;14901491 ensure!(amount >= value.into(), "Item balance not enouth");14921493 // update balance1494 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1495 .checked_sub(value)1496 .unwrap();1497 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14981499 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1500 .checked_add(value)1501 .unwrap();1502 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15031504 let old_owner = item.owner.clone();1505 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1506 let val64 = value.into();15071508 // transfer1509 if amount == val64 && !new_owner_has_account {1510 // change owner1511 // new owner do not have account1512 let mut new_full_item = full_item.clone();1513 new_full_item1514 .owner1515 .iter_mut()1516 .find(|i| i.owner == owner)1517 .unwrap()1518 .owner = new_owner.clone();1519 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15201521 // update index collection1522 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1523 } else {1524 let mut new_full_item = full_item.clone();1525 new_full_item1526 .owner1527 .iter_mut()1528 .find(|i| i.owner == owner)1529 .unwrap()1530 .fraction -= val64;15311532 // separate amount1533 if new_owner_has_account {1534 // new owner has account1535 new_full_item1536 .owner1537 .iter_mut()1538 .find(|i| i.owner == new_owner)1539 .unwrap()1540 .fraction += val64;1541 } else {1542 // new owner do not have account1543 new_full_item.owner.push(Ownership {1544 owner: new_owner.clone(),1545 fraction: val64,1546 });1547 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1548 }15491550 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1551 }15521553 Ok(())1554 }15551556 fn transfer_nft(1557 collection_id: u64,1558 item_id: u64,1559 sender: T::AccountId,1560 new_owner: T::AccountId,1561 ) -> DispatchResult {1562 ensure!(1563 <NftItemList<T>>::contains_key(collection_id, item_id),1564 "Item not exists"1565 );15661567 let mut item = <NftItemList<T>>::get(collection_id, item_id);15681569 ensure!(1570 sender == item.owner,1571 "sender parameter and item owner must be equal"1572 );15731574 // update balance1575 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1576 .checked_sub(1)1577 .unwrap();1578 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15791580 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1581 .checked_add(1)1582 .unwrap();1583 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15841585 // change owner1586 let old_owner = item.owner.clone();1587 item.owner = new_owner.clone();1588 <NftItemList<T>>::insert(collection_id, item_id, item);15891590 // update index collection1591 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15921593 // reset approved list1594 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1595 Ok(())1596 }15971598 fn init_collection(item: &CollectionType<T::AccountId>) {1599 // check params1600 assert!(1601 item.decimal_points <= 4,1602 "decimal_points parameter must be lower than 4"1603 );1604 assert!(1605 item.name.len() <= 64,1606 "Collection name can not be longer than 63 char"1607 );1608 assert!(1609 item.name.len() <= 256,1610 "Collection description can not be longer than 255 char"1611 );1612 assert!(1613 item.token_prefix.len() <= 16,1614 "Token prefix can not be longer than 15 char"1615 );16161617 // Generate next collection ID1618 let next_id = CreatedCollectionCount::get()1619 .checked_add(1)1620 .expect("collection id error");16211622 CreatedCollectionCount::put(next_id);1623 }16241625 fn init_nft_token(item: &NftItemType<T::AccountId>) {1626 let current_index = <ItemListIndex>::get(item.collection)1627 .checked_add(1)1628 .expect("Item list index id error");16291630 let item_owner = item.owner.clone();1631 let collection_id = item.collection.clone();1632 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16331634 <ItemListIndex>::insert(collection_id, current_index);16351636 // Update balance1637 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1638 .checked_add(1)1639 .unwrap();1640 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1641 }16421643 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1644 let current_index = <ItemListIndex>::get(item.collection)1645 .checked_add(1)1646 .expect("Item list index id error");1647 let owner = item.owner.clone();1648 let value = item.value as u64;16491650 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16511652 <ItemListIndex>::insert(item.collection, current_index);16531654 // Update balance1655 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1656 .checked_add(value)1657 .unwrap();1658 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1659 }16601661 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1662 let current_index = <ItemListIndex>::get(item.collection)1663 .checked_add(1)1664 .expect("Item list index id error");16651666 let value = item.owner.first().unwrap().fraction as u64;1667 let owner = item.owner.first().unwrap().owner.clone();16681669 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16701671 <ItemListIndex>::insert(item.collection, current_index);16721673 // Update balance1674 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1675 .checked_add(value)1676 .unwrap();1677 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1678 }16791680 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16811682 // add to account limit1683 if <AccountItemCount<T>>::contains_key(owner.clone()) {16841685 // bound Owned tokens by a single address1686 let count = <AccountItemCount<T>>::get(owner.clone());1687 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16881689 <AccountItemCount<T>>::insert(owner.clone(), 1690 count.checked_add(1).unwrap());1691 }1692 else {1693 <AccountItemCount<T>>::insert(owner.clone(), 1);1694 }16951696 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1697 if list_exists {1698 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1699 let item_contains = list.contains(&item_index.clone());17001701 if !item_contains {1702 list.push(item_index.clone());1703 }17041705 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1706 } else {1707 let mut itm = Vec::new();1708 itm.push(item_index.clone());1709 <AddressTokens<T>>::insert(collection_id, owner, itm);1710 1711 }17121713 Ok(())1714 }17151716 fn remove_token_index(1717 collection_id: u64,1718 item_index: u64,1719 owner: T::AccountId,1720 ) -> DispatchResult {17211722 // update counter1723 <AccountItemCount<T>>::insert(owner.clone(), 1724 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());172517261727 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1728 if list_exists {1729 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1730 let item_contains = list.contains(&item_index.clone());17311732 if item_contains {1733 list.retain(|&item| item != item_index);1734 <AddressTokens<T>>::insert(collection_id, owner, list);1735 }1736 }17371738 Ok(())1739 }17401741 fn move_token_index(1742 collection_id: u64,1743 item_index: u64,1744 old_owner: T::AccountId,1745 new_owner: T::AccountId,1746 ) -> DispatchResult {1747 Self::remove_token_index(collection_id, item_index, old_owner)?;1748 Self::add_token_index(collection_id, item_index, new_owner)?;17491750 Ok(())1751 }1752}17531754////////////////////////////////////////////////////////////////////////////////////////////////////1755// Economic models1756// #region17571758/// Fee multiplier.1759pub type Multiplier = FixedU128;17601761type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1762 <T as system::Trait>::AccountId,1763>>::Balance;1764type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1765 <T as system::Trait>::AccountId,1766>>::NegativeImbalance;17671768/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1769/// in the queue.1770#[derive(Encode, Decode, Clone, Eq, PartialEq)]1771pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1772 #[codec(compact)] BalanceOf<T>1773);17741775impl<T: Trait + Send + Sync> sp_std::fmt::Debug1776 for ChargeTransactionPayment<T>1777{1778 #[cfg(feature = "std")]1779 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1780 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1781 }1782 #[cfg(not(feature = "std"))]1783 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1784 Ok(())1785 }1786}17871788impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1789where1790 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1791 BalanceOf<T>: Send + Sync + FixedPointOperand,1792{1793 /// utility constructor. Used only in client/factory code.1794 pub fn from(fee: BalanceOf<T>) -> Self {1795 Self(fee)1796 }17971798 pub fn traditional_fee(1799 len: usize,1800 info: &DispatchInfoOf<T::Call>,1801 tip: BalanceOf<T>,1802 ) -> BalanceOf<T>1803 where1804 T::Call: Dispatchable<Info = DispatchInfo>,1805 {1806 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1807 }18081809 fn withdraw_fee(1810 &self,1811 who: &T::AccountId,1812 call: &T::Call,1813 info: &DispatchInfoOf<T::Call>,1814 len: usize,1815 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1816 let tip = self.0;18171818 // Set fee based on call type. Creating collection costs 1 Unique.1819 // All other transactions have traditional fees so far1820 // let fee = match call.is_sub_type() {1821 // Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1822 // _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1823 // // _ => <BalanceOf<T>>::from(100)1824 // };1825 let fee = Self::traditional_fee(len, info, tip);18261827 // Determine who is paying transaction fee based on ecnomic model1828 // Parse call to extract collection ID and access collection sponsor1829 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {1830 Some(Call::create_item(collection_id, _properties, _owner)) => {1831 <Collection<T>>::get(collection_id).sponsor1832 }1833 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1834 let _collection_mode = <Collection<T>>::get(collection_id).mode;18351836 // sponsor timeout1837 let sponsor_transfer = match _collection_mode {1838 CollectionMode::NFT(_) => {1839 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1840 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1841 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1842 if block_number >= limit_time {1843 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1844 true1845 }1846 else {1847 false1848 }1849 }1850 CollectionMode::Fungible(_) => {1851 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1852 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1853 if basket.iter().any(|i| i.address == _new_owner.clone())1854 {1855 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1856 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1857 if block_number >= limit_time {1858 basket.retain(|x| x.address == item.address);1859 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1860 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1861 true1862 }1863 else {1864 false1865 }1866 }1867 else {1868 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1869 true1870 }1871 }1872 CollectionMode::ReFungible(_, _) => {1873 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1874 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1875 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1876 if block_number >= limit_time {1877 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1878 true1879 } else {1880 false1881 }1882 }1883 _ => {1884 false1885 },1886 };18871888 if !sponsor_transfer {1889 T::AccountId::default()1890 } else {1891 <Collection<T>>::get(collection_id).sponsor1892 }1893 }18941895 _ => T::AccountId::default(),1896 };18971898 // Sponsor smart contracts1899 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {19001901 // On instantiation: set the contract owner1902 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {19031904 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(1905 code_hash,1906 &data,1907 &who,1908 );1909 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());19101911 T::AccountId::default()1912 },19131914 // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is1915 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {19161917 let mut sp = T::AccountId::default();1918 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());1919 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {1920 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {1921 sp = called_contract;1922 }1923 }19241925 sp1926 },19271928 _ => sponsor,1929 };19301931 let mut who_pays_fee: T::AccountId = sponsor.clone();1932 if sponsor == T::AccountId::default() {1933 who_pays_fee = who.clone();1934 }19351936 // Only mess with balances if fee is not zero.1937 if fee.is_zero() {1938 return Ok((fee, None));1939 }19401941 match <T as transaction_payment::Trait>::Currency::withdraw(1942 &who_pays_fee,1943 fee,1944 if tip.is_zero() {1945 WithdrawReason::TransactionPayment.into()1946 } else {1947 WithdrawReason::TransactionPayment | WithdrawReason::Tip1948 },1949 ExistenceRequirement::KeepAlive,1950 ) {1951 Ok(imbalance) => Ok((fee, Some(imbalance))),1952 Err(_) => Err(InvalidTransaction::Payment.into()),1953 }1954 }1955}195619571958impl<T: Trait + Send + Sync> SignedExtension1959 for ChargeTransactionPayment<T>1960where1961 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1962 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1963{1964 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1965 type AccountId = T::AccountId;1966 type Call = T::Call;1967 type AdditionalSigned = ();1968 type Pre = (1969 BalanceOf<T>,1970 Self::AccountId,1971 Option<NegativeImbalanceOf<T>>,1972 BalanceOf<T>,1973 );1974 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1975 Ok(())1976 }19771978 fn validate(1979 &self,1980 _who: &Self::AccountId,1981 _call: &Self::Call,1982 _info: &DispatchInfoOf<Self::Call>,1983 _len: usize,1984 ) -> TransactionValidity {1985 Ok(ValidTransaction::default())1986 }19871988 fn pre_dispatch(1989 self,1990 who: &Self::AccountId,1991 call: &Self::Call,1992 info: &DispatchInfoOf<Self::Call>,1993 len: usize,1994 ) -> Result<Self::Pre, TransactionValidityError> {1995 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1996 Ok((self.0, who.clone(), imbalance, fee))1997 }19981999 fn post_dispatch(2000 pre: Self::Pre,2001 info: &DispatchInfoOf<Self::Call>,2002 post_info: &PostDispatchInfoOf<Self::Call>,2003 len: usize,2004 _result: &DispatchResult,2005 ) -> Result<(), TransactionValidityError> {2006 let (tip, who, imbalance, fee) = pre;2007 if let Some(payed) = imbalance {2008 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2009 len as u32, info, post_info, tip,2010 );2011 let refund = fee.saturating_sub(actual_fee);2012 let actual_payment =2013 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2014 &who, refund,2015 ) {2016 Ok(refund_imbalance) => {2017 // The refund cannot be larger than the up front payed max weight.2018 // `PostDispatchInfo::calc_unspent` guards against such a case.2019 match payed.offset(refund_imbalance) {2020 Ok(actual_payment) => actual_payment,2021 Err(_) => return Err(InvalidTransaction::Payment.into()),2022 }2023 }2024 // We do not recreate the account using the refund. The up front payment2025 // is gone in that case.2026 Err(_) => payed,2027 };2028 let imbalances = actual_payment.split(tip);2029 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2030 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2031 );2032 }2033 Ok(())2034 }2035}20362037// #endregion203820391#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage,12 dispatch::DispatchResult,13 ensure, parameter_types, fail,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30 traits::{31 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,32 },33 transaction_validity::{34 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35 },36 FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849// Structs50// #region5152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55 Invalid,56 NFT,57 // decimal points58 Fungible(u32),59 // decimal points60 ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64 fn into(self) -> u8 {65 match self {66 CollectionMode::Invalid => 0,67 CollectionMode::NFT => 1,68 CollectionMode::Fungible(_) => 2,69 CollectionMode::ReFungible(_) => 3,70 }71 }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77 Normal,78 WhiteList,79}80impl Default for AccessMode {81 fn default() -> Self {82 Self::Normal83 }84}8586impl Default for CollectionMode {87 fn default() -> Self {88 Self::Invalid89 }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95 pub owner: AccountId,96 pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102 pub owner: AccountId,103 pub mode: CollectionMode,104 pub access: AccessMode,105 pub decimal_points: u32,106 pub name: Vec<u16>, // 64 include null escape char107 pub description: Vec<u16>, // 256 include null escape char108 pub token_prefix: Vec<u8>, // 16 include null escape char109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender112 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship113 pub variable_on_chain_schema: Vec<u8>, //114 pub const_on_chain_schema: Vec<u8>, //115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120 pub admin: AccountId,121 pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127 pub collection: u64,128 pub owner: AccountId,129 pub const_data: Vec<u8>,130 pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136 pub collection: u64,137 pub owner: AccountId,138 pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144 pub collection: u64,145 pub owner: Vec<Ownership<AccountId>>,146 pub const_data: Vec<u8>,147 pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153 pub approved: AccountId,154 pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160 pub sender: AccountId,161 pub recipient: AccountId,162 pub collection_id: u64,163 pub item_id: u64,164 pub amount: u64,165 pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171 pub address: AccountId,172 pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178 pub collection_numbers_limit: u64,179 pub account_token_ownership_limit: u64,180 pub collections_admins_limit: u64,181 pub custom_data_limit: u32,182183 // Timeouts for item types in passed blocks184 pub nft_sponsor_transfer_timeout: u32,185 pub fungible_sponsor_transfer_timeout: u32,186 pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190 fn create_collection() -> Weight;191 fn destroy_collection() -> Weight;192 fn add_to_white_list() -> Weight;193 fn remove_from_white_list() -> Weight;194 fn set_public_access_mode() -> Weight;195 fn set_mint_permission() -> Weight;196 fn change_collection_owner() -> Weight;197 fn add_collection_admin() -> Weight;198 fn remove_collection_admin() -> Weight;199 fn set_collection_sponsor() -> Weight;200 fn confirm_sponsorship() -> Weight;201 fn remove_collection_sponsor() -> Weight;202 fn create_item(s: usize) -> Weight;203 fn burn_item() -> Weight;204 fn transfer() -> Weight;205 fn approve() -> Weight;206 fn transfer_from() -> Weight;207 fn set_offchain_schema() -> Weight;208 fn set_const_on_chain_schema() -> Weight;209 fn set_variable_on_chain_schema() -> Weight;210 fn set_variable_meta_data() -> Weight;211 // fn enable_contract_sponsoring() -> Weight;212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CreateNftData {217 pub const_data: Vec<u8>,218 pub variable_data: Vec<u8>,219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateFungibleData {224}225226#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228pub struct CreateReFungibleData {229 pub const_data: Vec<u8>,230 pub variable_data: Vec<u8>,231}232233#[derive(Encode, Decode, Debug, Clone, PartialEq)]234#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]235pub enum CreateItemData {236 NFT(CreateNftData),237 Fungible(CreateFungibleData),238 ReFungible(CreateReFungibleData)239}240241impl CreateItemData {242 pub fn len(&self) -> usize {243 let len = match self {244 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),245 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),246 _ => 0247 };248 249 return len;250 }251}252253impl From<CreateNftData> for CreateItemData {254 fn from(item: CreateNftData) -> Self {255 CreateItemData::NFT(item)256 }257}258259impl From<CreateReFungibleData> for CreateItemData {260 fn from(item: CreateReFungibleData) -> Self {261 CreateItemData::ReFungible(item)262 }263}264265impl From<CreateFungibleData> for CreateItemData {266 fn from(item: CreateFungibleData) -> Self {267 CreateItemData::Fungible(item)268 }269}270271pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {272 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;273274 /// Weight information for extrinsics in this pallet.275 type WeightInfo: WeightInfo;276}277278#[cfg(feature = "runtime-benchmarks")]279mod benchmarking;280281// #endregion282283decl_storage! {284 trait Store for Module<T: Trait> as Nft {285286 // Private members287 NextCollectionID: u64;288 CreatedCollectionCount: u64;289 ChainVersion: u64;290 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;291292 // Chain limits struct293 pub ChainLimit get(fn chain_limit) config(): ChainLimits;294295 // Bound counters296 CollectionCount: u64;297 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;298299 // Basic collections300 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;301 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;302 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;303304 /// Balance owner per collection map305 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;306307 /// second parameter: item id + owner account id308 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;309310 /// Item collections311 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;312 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;313 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;314315 /// Index list316 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;317318 /// Tokens transfer baskets319 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;320 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;321 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;322323 // Contract Sponsorship and Ownership324 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;325 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;326 }327 add_extra_genesis {328 build(|config: &GenesisConfig<T>| {329 // Modification of storage330 for (_num, _c) in &config.collection {331 <Module<T>>::init_collection(_c);332 }333334 for (_num, _q, _i) in &config.nft_item_id {335 <Module<T>>::init_nft_token(_i);336 }337338 for (_num, _q, _i) in &config.fungible_item_id {339 <Module<T>>::init_fungible_token(_i);340 }341342 for (_num, _q, _i) in &config.refungible_item_id {343 <Module<T>>::init_refungible_token(_i);344 }345 })346 }347}348349decl_event!(350 pub enum Event<T>351 where352 AccountId = <T as system::Trait>::AccountId,353 {354 /// New collection was created355 /// 356 /// # Arguments357 /// 358 /// * collection_id: Globally unique identifier of newly created collection.359 /// 360 /// * mode: [CollectionMode] converted into u8.361 /// 362 /// * account_id: Collection owner.363 Created(u64, u8, AccountId),364365 /// New item was created.366 /// 367 /// # Arguments368 /// 369 /// * collection_id: Id of the collection where item was created.370 /// 371 /// * item_id: Id of an item. Unique within the collection.372 ItemCreated(u64, u64),373374 /// Collection item was burned.375 /// 376 /// # Arguments377 /// 378 /// collection_id.379 /// 380 /// item_id: Identifier of burned NFT.381 ItemDestroyed(u64, u64),382 }383);384385decl_module! {386 pub struct Module<T: Trait> for enum Call where origin: T::Origin {387388 fn deposit_event() = default;389390 fn on_initialize(now: T::BlockNumber) -> Weight {391392 if ChainVersion::get() < 2393 {394 let value = NextCollectionID::get();395 CreatedCollectionCount::put(value);396 ChainVersion::put(2);397 }398399 0400 }401402 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.403 /// 404 /// # Permissions405 /// 406 /// * Anyone.407 /// 408 /// # Arguments409 /// 410 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.411 /// 412 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.413 /// 414 /// * token_prefix: UTF-8 string with token prefix.415 /// 416 /// * mode: [CollectionMode] collection type and type dependent data.417 // returns collection ID418 #[weight = T::WeightInfo::create_collection()]419 pub fn create_collection(origin,420 collection_name: Vec<u16>,421 collection_description: Vec<u16>,422 token_prefix: Vec<u8>,423 mode: CollectionMode) -> DispatchResult {424425 // Anyone can create a collection426 let who = ensure_signed(origin)?;427428 let decimal_points = match mode {429 CollectionMode::Fungible(points) => points,430 CollectionMode::ReFungible(points) => points,431 _ => 0432 };433434 // bound Total number of collections435 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");436437 // check params438 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");439440 let mut name = collection_name.to_vec();441 name.push(0);442 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");443444 let mut description = collection_description.to_vec();445 description.push(0);446 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");447448 let mut prefix = token_prefix.to_vec();449 prefix.push(0);450 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");451452 // Generate next collection ID453 let next_id = CreatedCollectionCount::get()454 .checked_add(1)455 .expect("collection id error");456457 // bound counter458 let total = CollectionCount::get()459 .checked_add(1)460 .expect("collection counter error");461462 CreatedCollectionCount::put(next_id);463 CollectionCount::put(total);464465 // Create new collection466 let new_collection = CollectionType {467 owner: who.clone(),468 name: name,469 mode: mode.clone(),470 mint_mode: false,471 access: AccessMode::Normal,472 description: description,473 decimal_points: decimal_points,474 token_prefix: prefix,475 offchain_schema: Vec::new(),476 sponsor: T::AccountId::default(),477 unconfirmed_sponsor: T::AccountId::default(),478 variable_on_chain_schema: Vec::new(),479 const_on_chain_schema: Vec::new(),480 };481482 // Add new collection to map483 <Collection<T>>::insert(next_id, new_collection);484485 // call event486 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));487488 Ok(())489 }490491 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.492 /// 493 /// # Permissions494 /// 495 /// * Collection Owner.496 /// 497 /// # Arguments498 /// 499 /// * collection_id: collection to destroy.500 #[weight = T::WeightInfo::destroy_collection()]501 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {502503 let sender = ensure_signed(origin)?;504 Self::check_owner_permissions(collection_id, sender)?;505506 <AddressTokens<T>>::remove_prefix(collection_id);507 <ApprovedList<T>>::remove_prefix(collection_id);508 <Balance<T>>::remove_prefix(collection_id);509 <ItemListIndex>::remove(collection_id);510 <AdminList<T>>::remove(collection_id);511 <Collection<T>>::remove(collection_id);512 <WhiteList<T>>::remove(collection_id);513514 <NftItemList<T>>::remove_prefix(collection_id);515 <FungibleItemList<T>>::remove_prefix(collection_id);516 <ReFungibleItemList<T>>::remove_prefix(collection_id);517518 <NftTransferBasket<T>>::remove_prefix(collection_id);519 <FungibleTransferBasket<T>>::remove_prefix(collection_id);520 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);521522 if CollectionCount::get() > 0523 {524 // bound couter525 let total = CollectionCount::get()526 .checked_sub(1)527 .expect("collection counter error");528529 CollectionCount::put(total);530 }531532 Ok(())533 }534535 /// Add an address to white list.536 /// 537 /// # Permissions538 /// 539 /// * Collection Owner540 /// * Collection Admin541 /// 542 /// # Arguments543 /// 544 /// * collection_id.545 /// 546 /// * address.547 #[weight = T::WeightInfo::add_to_white_list()]548 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{549550 let sender = ensure_signed(origin)?;551 Self::check_owner_or_admin_permissions(collection_id, sender)?;552553 let mut white_list_collection: Vec<T::AccountId>;554 if <WhiteList<T>>::contains_key(collection_id) {555 white_list_collection = <WhiteList<T>>::get(collection_id);556 if !white_list_collection.contains(&address.clone())557 {558 white_list_collection.push(address.clone());559 }560 }561 else {562 white_list_collection = Vec::new();563 white_list_collection.push(address.clone());564 }565566 <WhiteList<T>>::insert(collection_id, white_list_collection);567 Ok(())568 }569570 /// Remove an address from white list.571 /// 572 /// # Permissions573 /// 574 /// * Collection Owner575 /// * Collection Admin576 /// 577 /// # Arguments578 /// 579 /// * collection_id.580 /// 581 /// * address.582 #[weight = T::WeightInfo::remove_from_white_list()]583 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{584585 let sender = ensure_signed(origin)?;586 Self::check_owner_or_admin_permissions(collection_id, sender)?;587588 if <WhiteList<T>>::contains_key(collection_id) {589 let mut white_list_collection = <WhiteList<T>>::get(collection_id);590 if white_list_collection.contains(&address.clone())591 {592 white_list_collection.retain(|i| *i != address.clone());593 <WhiteList<T>>::insert(collection_id, white_list_collection);594 }595 }596597 Ok(())598 }599600 /// Toggle between normal and white list access for the methods with access for `Anyone`.601 /// 602 /// # Permissions603 /// 604 /// * Collection Owner.605 /// 606 /// # Arguments607 /// 608 /// * collection_id.609 /// 610 /// * mode: [AccessMode]611 #[weight = T::WeightInfo::set_public_access_mode()]612 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult613 {614 let sender = ensure_signed(origin)?;615616 Self::check_owner_permissions(collection_id, sender)?;617 let mut target_collection = <Collection<T>>::get(collection_id);618 target_collection.access = mode;619 <Collection<T>>::insert(collection_id, target_collection);620621 Ok(())622 }623624 /// Allows Anyone to create tokens if:625 /// * White List is enabled, and626 /// * Address is added to white list, and627 /// * This method was called with True parameter628 /// 629 /// # Permissions630 /// * Collection Owner631 ///632 /// # Arguments633 /// 634 /// * collection_id.635 /// 636 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.637 #[weight = T::WeightInfo::set_mint_permission()]638 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult639 {640 let sender = ensure_signed(origin)?;641642 Self::check_owner_permissions(collection_id, sender)?;643 let mut target_collection = <Collection<T>>::get(collection_id);644 target_collection.mint_mode = mint_permission;645 <Collection<T>>::insert(collection_id, target_collection);646647 Ok(())648 }649650 /// Change the owner of the collection.651 /// 652 /// # Permissions653 /// 654 /// * Collection Owner.655 /// 656 /// # Arguments657 /// 658 /// * collection_id.659 /// 660 /// * new_owner.661 #[weight = T::WeightInfo::change_collection_owner()]662 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {663664 let sender = ensure_signed(origin)?;665 Self::check_owner_permissions(collection_id, sender)?;666 let mut target_collection = <Collection<T>>::get(collection_id);667 target_collection.owner = new_owner;668 <Collection<T>>::insert(collection_id, target_collection);669670 Ok(())671 }672673 /// Adds an admin of the Collection.674 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 675 /// 676 /// # Permissions677 /// 678 /// * Collection Owner.679 /// * Collection Admin.680 /// 681 /// # Arguments682 /// 683 /// * collection_id: ID of the Collection to add admin for.684 /// 685 /// * new_admin_id: Address of new admin to add.686 #[weight = T::WeightInfo::add_collection_admin()]687 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {688689 let sender = ensure_signed(origin)?;690 Self::check_owner_or_admin_permissions(collection_id, sender)?;691 let mut admin_arr: Vec<T::AccountId> = Vec::new();692693 if <AdminList<T>>::contains_key(collection_id)694 {695 admin_arr = <AdminList<T>>::get(collection_id);696 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");697 }698699 // Number of collection admins700 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");701702 admin_arr.push(new_admin_id);703 <AdminList<T>>::insert(collection_id, admin_arr);704705 Ok(())706 }707708 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.709 ///710 /// # Permissions711 /// 712 /// * Collection Owner.713 /// * Collection Admin.714 /// 715 /// # Arguments716 /// 717 /// * collection_id: ID of the Collection to remove admin for.718 /// 719 /// * account_id: Address of admin to remove.720 #[weight = T::WeightInfo::remove_collection_admin()]721 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {722723 let sender = ensure_signed(origin)?;724 Self::check_owner_or_admin_permissions(collection_id, sender)?;725726 if <AdminList<T>>::contains_key(collection_id)727 {728 let mut admin_arr = <AdminList<T>>::get(collection_id);729 admin_arr.retain(|i| *i != account_id);730 <AdminList<T>>::insert(collection_id, admin_arr);731 }732733 Ok(())734 }735736 /// # Permissions737 /// 738 /// * Collection Owner739 /// 740 /// # Arguments741 /// 742 /// * collection_id.743 /// 744 /// * new_sponsor.745 #[weight = T::WeightInfo::set_collection_sponsor()]746 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {747748 let sender = ensure_signed(origin)?;749 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");750751 let mut target_collection = <Collection<T>>::get(collection_id);752 ensure!(sender == target_collection.owner, "You do not own this collection");753754 target_collection.unconfirmed_sponsor = new_sponsor;755 <Collection<T>>::insert(collection_id, target_collection);756757 Ok(())758 }759760 /// # Permissions761 /// 762 /// * Sponsor.763 /// 764 /// # Arguments765 /// 766 /// * collection_id.767 #[weight = T::WeightInfo::confirm_sponsorship()]768 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {769770 let sender = ensure_signed(origin)?;771 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");772773 let mut target_collection = <Collection<T>>::get(collection_id);774 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");775776 target_collection.sponsor = target_collection.unconfirmed_sponsor;777 target_collection.unconfirmed_sponsor = T::AccountId::default();778 <Collection<T>>::insert(collection_id, target_collection);779780 Ok(())781 }782783 /// Switch back to pay-per-own-transaction model.784 ///785 /// # Permissions786 ///787 /// * Collection owner.788 /// 789 /// # Arguments790 /// 791 /// * collection_id.792 #[weight = T::WeightInfo::remove_collection_sponsor()]793 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {794795 let sender = ensure_signed(origin)?;796 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");797798 let mut target_collection = <Collection<T>>::get(collection_id);799 ensure!(sender == target_collection.owner, "You do not own this collection");800801 target_collection.sponsor = T::AccountId::default();802 <Collection<T>>::insert(collection_id, target_collection);803804 Ok(())805 }806807 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.808 /// 809 /// # Permissions810 /// 811 /// * Collection Owner.812 /// * Collection Admin.813 /// * Anyone if814 /// * White List is enabled, and815 /// * Address is added to white list, and816 /// * MintPermission is enabled (see SetMintPermission method)817 /// 818 /// # Arguments819 /// 820 /// * collection_id: ID of the collection.821 /// 822 /// * owner: Address, initial owner of the NFT.823 ///824 /// * data: Token data to store on chain.825 // #[weight =826 // (130_000_000 as Weight)827 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))828 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))829 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]830831 #[weight = T::WeightInfo::create_item(data.len())]832 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {833834 let sender = ensure_signed(origin)?;835 Self::collection_exists(collection_id)?;836 let target_collection = <Collection<T>>::get(collection_id);837838 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {839 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");840 Self::check_white_list(collection_id, &owner)?;841 Self::check_white_list(collection_id, &sender)?;842 }843844 match target_collection.mode845 {846 CollectionMode::NFT => {847 if let CreateItemData::NFT(data) = data {848 // check sizes849 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");850 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");851 852 // Create nft item853 let item = NftItemType {854 collection: collection_id,855 owner: owner,856 const_data: data.const_data.clone(),857 variable_data: data.variable_data.clone() 858 };859 860 Self::add_nft_item(item)?;861 862 } else {863 fail!("Not NFT item data used to mint in NFT collection.");864 }865 },866 CollectionMode::Fungible(_) => {867 if let CreateItemData::Fungible(_) = data {868 869 let item = FungibleItemType {870 collection: collection_id,871 owner: owner,872 value: (10 as u128).pow(target_collection.decimal_points)873 };874 875 Self::add_fungible_item(item)?;876 } else {877 fail!("Not Fungible item data used to mint in Fungible collection.");878 }879 },880 CollectionMode::ReFungible(_) => {881 if let CreateItemData::ReFungible(data) = data {882 883 // check sizes884 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");885 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");886 887 let mut owner_list = Vec::new();888 let value = (10 as u128).pow(target_collection.decimal_points);889 owner_list.push(Ownership {owner: owner.clone(), fraction: value});890 891 let item = ReFungibleItemType {892 collection: collection_id,893 owner: owner_list,894 const_data: data.const_data.clone(),895 variable_data: data.variable_data.clone() 896 };897 898 Self::add_refungible_item(item)?;899 } else {900 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");901 }902 },903 _ => { ensure!(1 == 0,"Unexpected collection type."); }904 };905906 // call event907 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));908909 Ok(())910 }911912 /// Destroys a concrete instance of NFT.913 /// 914 /// # Permissions915 /// 916 /// * Collection Owner.917 /// * Collection Admin.918 /// * Current NFT Owner.919 /// 920 /// # Arguments921 /// 922 /// * collection_id: ID of the collection.923 /// 924 /// * item_id: ID of NFT to burn.925 #[weight = T::WeightInfo::burn_item()]926 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {927928 let sender = ensure_signed(origin)?;929 Self::collection_exists(collection_id)?;930931 // Transfer permissions check932 let target_collection = <Collection<T>>::get(collection_id);933 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||934 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),935 "Only item owner, collection owner and admins can modify item");936937 if target_collection.access == AccessMode::WhiteList {938 Self::check_white_list(collection_id, &sender)?;939 }940941 match target_collection.mode942 {943 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,944 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,945 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,946 _ => ()947 };948949 // call event950 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));951952 Ok(())953 }954955 /// Change ownership of the token.956 /// 957 /// # Permissions958 /// 959 /// * Collection Owner960 /// * Collection Admin961 /// * Current NFT owner962 ///963 /// # Arguments964 /// 965 /// * recipient: Address of token recipient.966 /// 967 /// * collection_id.968 /// 969 /// * item_id: ID of the item970 /// * Non-Fungible Mode: Required.971 /// * Fungible Mode: Ignored.972 /// * Re-Fungible Mode: Required.973 /// 974 /// * value: Amount to transfer.975 /// * Non-Fungible Mode: Ignored976 /// * Fungible Mode: Must specify transferred amount977 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)978 #[weight = T::WeightInfo::transfer()]979 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {980981 let sender = ensure_signed(origin)?;982983 // Transfer permissions check984 let target_collection = <Collection<T>>::get(collection_id);985 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||986 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),987 "Only item owner, collection owner and admins can modify item");988989 if target_collection.access == AccessMode::WhiteList {990 Self::check_white_list(collection_id, &sender)?;991 Self::check_white_list(collection_id, &recipient)?;992 }993994 match target_collection.mode995 {996 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,997 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,998 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,999 _ => ()1000 };10011002 Ok(())1003 }10041005 /// Set, change, or remove approved address to transfer the ownership of the NFT.1006 /// 1007 /// # Permissions1008 /// 1009 /// * Collection Owner1010 /// * Collection Admin1011 /// * Current NFT owner1012 /// 1013 /// # Arguments1014 /// 1015 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1016 /// 1017 /// * collection_id.1018 /// 1019 /// * item_id: ID of the item.1020 #[weight = T::WeightInfo::approve()]1021 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10221023 let sender = ensure_signed(origin)?;10241025 // Transfer permissions check1026 let target_collection = <Collection<T>>::get(collection_id);1027 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1028 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1029 "Only item owner, collection owner and admins can approve");10301031 if target_collection.access == AccessMode::WhiteList {1032 Self::check_white_list(collection_id, &sender)?;1033 Self::check_white_list(collection_id, &approved)?;1034 }10351036 // amount param stub1037 let amount = 100000000;10381039 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1040 if list_exists {10411042 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1043 let item_contains = list.iter().any(|i| i.approved == approved);10441045 if !item_contains {1046 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1047 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1048 }1049 } else {10501051 let mut list = Vec::new();1052 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1053 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1054 }10551056 Ok(())1057 }1058 1059 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1060 /// 1061 /// # Permissions1062 /// * Collection Owner1063 /// * Collection Admin1064 /// * Current NFT owner1065 /// * Address approved by current NFT owner1066 /// 1067 /// # Arguments1068 /// 1069 /// * from: Address that owns token.1070 /// 1071 /// * recipient: Address of token recipient.1072 /// 1073 /// * collection_id.1074 /// 1075 /// * item_id: ID of the item.1076 /// 1077 /// * value: Amount to transfer.1078 #[weight = T::WeightInfo::transfer_from()]1079 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10801081 let sender = ensure_signed(origin)?;1082 let mut appoved_transfer = false;10831084 // Check approve1085 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1086 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1087 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1088 if opt_item.is_some()1089 {1090 appoved_transfer = true;1091 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1092 }1093 }10941095 // Transfer permissions check1096 let target_collection = <Collection<T>>::get(collection_id);1097 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1098 "Only item owner, collection owner and admins can modify items");10991100 if target_collection.access == AccessMode::WhiteList {1101 Self::check_white_list(collection_id, &sender)?;1102 Self::check_white_list(collection_id, &recipient)?;1103 }11041105 // remove approve1106 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1107 .into_iter().filter(|i| i.approved != sender.clone()).collect();1108 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);110911101111 match target_collection.mode1112 {1113 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1114 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1115 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1116 _ => ()1117 };11181119 Ok(())1120 }11211122 ///1123 #[weight = 0]1124 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11251126 // let no_perm_mes = "You do not have permissions to modify this collection";1127 // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1128 // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1129 // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11301131 // // on_nft_received call11321133 // Self::transfer(origin, collection_id, item_id, new_owner)?;11341135 Ok(())1136 }1137 1138 /// Set off-chain data schema.1139 /// 1140 /// # Permissions1141 /// 1142 /// * Collection Owner1143 /// * Collection Admin1144 /// 1145 /// # Arguments1146 /// 1147 /// * collection_id.1148 /// 1149 /// * schema: String representing the offchain data schema.1150 #[weight = T::WeightInfo::set_variable_meta_data()]1151 pub fn set_variable_meta_data (1152 origin,1153 collection_id: u64,1154 item_id: u64,1155 data: Vec<u8>1156 ) -> DispatchResult {1157 let sender = ensure_signed(origin)?;1158 1159 Self::collection_exists(collection_id)?;11601161 // Modify permissions check1162 let target_collection = <Collection<T>>::get(collection_id);1163 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1164 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1165 "Only item owner, collection owner and admins can modify item");11661167 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11681169 match target_collection.mode1170 {1171 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1172 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1173 _ => ()1174 };11751176 Ok(())1177 }1178 11791180 /// Set off-chain data schema.1181 /// 1182 /// # Permissions1183 /// 1184 /// * Collection Owner1185 /// * Collection Admin1186 /// 1187 /// # Arguments1188 /// 1189 /// * collection_id.1190 /// 1191 /// * schema: String representing the offchain data schema.1192 #[weight = T::WeightInfo::set_offchain_schema()]1193 pub fn set_offchain_schema(1194 origin,1195 collection_id: u64,1196 schema: Vec<u8>1197 ) -> DispatchResult {1198 let sender = ensure_signed(origin)?;1199 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12001201 let mut target_collection = <Collection<T>>::get(collection_id);1202 target_collection.offchain_schema = schema;1203 <Collection<T>>::insert(collection_id, target_collection);12041205 Ok(())1206 }12071208 /// Set const on-chain data schema.1209 /// 1210 /// # Permissions1211 /// 1212 /// * Collection Owner1213 /// * Collection Admin1214 /// 1215 /// # Arguments1216 /// 1217 /// * collection_id.1218 /// 1219 /// * schema: String representing the const on-chain data schema.1220 #[weight = T::WeightInfo::set_const_on_chain_schema()]1221 pub fn set_const_on_chain_schema (1222 origin,1223 collection_id: u64,1224 schema: Vec<u8>1225 ) -> DispatchResult {1226 let sender = ensure_signed(origin)?;1227 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12281229 let mut target_collection = <Collection<T>>::get(collection_id);1230 target_collection.const_on_chain_schema = schema;1231 <Collection<T>>::insert(collection_id, target_collection);12321233 Ok(())1234 }12351236 /// Set variable on-chain data schema.1237 /// 1238 /// # Permissions1239 /// 1240 /// * Collection Owner1241 /// * Collection Admin1242 /// 1243 /// # Arguments1244 /// 1245 /// * collection_id.1246 /// 1247 /// * schema: String representing the variable on-chain data schema.1248 #[weight = T::WeightInfo::set_const_on_chain_schema()]1249 pub fn set_variable_on_chain_schema (1250 origin,1251 collection_id: u64,1252 schema: Vec<u8>1253 ) -> DispatchResult {1254 let sender = ensure_signed(origin)?;1255 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12561257 let mut target_collection = <Collection<T>>::get(collection_id);1258 target_collection.variable_on_chain_schema = schema;1259 <Collection<T>>::insert(collection_id, target_collection);12601261 Ok(())1262 }12631264 // Sudo permissions function1265 #[weight = 0]1266 pub fn set_chain_limits(1267 origin,1268 limits: ChainLimits1269 ) -> DispatchResult {1270 ensure_root(origin)?;1271 <ChainLimit>::put(limits);1272 Ok(())1273 }12741275 /// Enable smart contract self-sponsoring.1276 /// 1277 /// # Permissions1278 /// 1279 /// * Contract Owner1280 /// 1281 /// # Arguments1282 /// 1283 /// * contract address1284 /// * enable flag1285 /// 1286 #[weight = 0]1287 pub fn enable_contract_sponsoring(1288 origin,1289 contract_address: T::AccountId,1290 enable: bool1291 ) -> DispatchResult {1292 let sender = ensure_signed(origin)?;1293 let mut is_owner = false;1294 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1295 let owner = <ContractOwner<T>>::get(&contract_address);1296 is_owner = sender == owner;1297 }1298 ensure!(is_owner, "Only contract owner may call this method");12991300 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1301 Ok(())1302 }13031304 }1305}13061307impl<T: Trait> Module<T> {1308 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1309 let current_index = <ItemListIndex>::get(item.collection)1310 .checked_add(1)1311 .expect("Item list index id error");1312 let itemcopy = item.clone();1313 let owner = item.owner.clone();1314 let value = item.value as u64;13151316 Self::add_token_index(item.collection, current_index, owner.clone())?;13171318 <ItemListIndex>::insert(item.collection, current_index);1319 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13201321 // Add current block1322 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1323 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1324 1325 // Update balance1326 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1327 .checked_add(value)1328 .unwrap();1329 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13301331 Ok(())1332 }13331334 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1335 let current_index = <ItemListIndex>::get(item.collection)1336 .checked_add(1)1337 .expect("Item list index id error");1338 let itemcopy = item.clone();13391340 let value = item.owner.first().unwrap().fraction as u64;1341 let owner = item.owner.first().unwrap().owner.clone();13421343 Self::add_token_index(item.collection, current_index, owner.clone())?;13441345 <ItemListIndex>::insert(item.collection, current_index);1346 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13471348 // Add current block1349 let block_number: T::BlockNumber = 0.into();1350 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13511352 // Update balance1353 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1354 .checked_add(value)1355 .unwrap();1356 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13571358 Ok(())1359 }13601361 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1362 let current_index = <ItemListIndex>::get(item.collection)1363 .checked_add(1)1364 .expect("Item list index id error");13651366 let item_owner = item.owner.clone();1367 let collection_id = item.collection.clone();1368 Self::add_token_index(collection_id, current_index, item.owner.clone())?;13691370 <ItemListIndex>::insert(collection_id, current_index);1371 <NftItemList<T>>::insert(collection_id, current_index, item);13721373 // Add current block1374 let block_number: T::BlockNumber = 0.into();1375 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13761377 // Update balance1378 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1379 .checked_add(1)1380 .unwrap();1381 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13821383 Ok(())1384 }13851386 fn burn_refungible_item(1387 collection_id: u64,1388 item_id: u64,1389 owner: T::AccountId,1390 ) -> DispatchResult {1391 ensure!(1392 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1393 "Item does not exists"1394 );1395 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1396 let item = collection1397 .owner1398 .iter()1399 .filter(|&i| i.owner == owner)1400 .next()1401 .unwrap();1402 Self::remove_token_index(collection_id, item_id, owner.clone())?;14031404 // remove approve list1405 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14061407 // update balance1408 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1409 .checked_sub(item.fraction as u64)1410 .unwrap();1411 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14121413 <ReFungibleItemList<T>>::remove(collection_id, item_id);14141415 Ok(())1416 }14171418 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1419 ensure!(1420 <NftItemList<T>>::contains_key(collection_id, item_id),1421 "Item does not exists"1422 );1423 let item = <NftItemList<T>>::get(collection_id, item_id);1424 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14251426 // remove approve list1427 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14281429 // update balance1430 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1431 .checked_sub(1)1432 .unwrap();1433 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1434 <NftItemList<T>>::remove(collection_id, item_id);14351436 Ok(())1437 }14381439 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1440 ensure!(1441 <FungibleItemList<T>>::contains_key(collection_id, item_id),1442 "Item does not exists"1443 );1444 let item = <FungibleItemList<T>>::get(collection_id, item_id);1445 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14461447 // remove approve list1448 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14491450 // update balance1451 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1452 .checked_sub(item.value as u64)1453 .unwrap();1454 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14551456 <FungibleItemList<T>>::remove(collection_id, item_id);14571458 Ok(())1459 }14601461 fn collection_exists(collection_id: u64) -> DispatchResult {1462 ensure!(1463 <Collection<T>>::contains_key(collection_id),1464 "This collection does not exist"1465 );1466 Ok(())1467 }14681469 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1470 Self::collection_exists(collection_id)?;14711472 let target_collection = <Collection<T>>::get(collection_id);1473 ensure!(1474 subject == target_collection.owner,1475 "You do not own this collection"1476 );14771478 Ok(())1479 }14801481 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1482 let target_collection = <Collection<T>>::get(collection_id);1483 let mut result: bool = subject == target_collection.owner;1484 let exists = <AdminList<T>>::contains_key(collection_id);14851486 if !result & exists {1487 if <AdminList<T>>::get(collection_id).contains(&subject) {1488 result = true1489 }1490 }14911492 result1493 }14941495 fn check_owner_or_admin_permissions(1496 collection_id: u64,1497 subject: T::AccountId,1498 ) -> DispatchResult {1499 Self::collection_exists(collection_id)?;1500 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15011502 ensure!(1503 result,1504 "You do not have permissions to modify this collection"1505 );1506 Ok(())1507 }15081509 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1510 let target_collection = <Collection<T>>::get(collection_id);15111512 match target_collection.mode {1513 CollectionMode::NFT => {1514 <NftItemList<T>>::get(collection_id, item_id).owner == subject1515 }1516 CollectionMode::Fungible(_) => {1517 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1518 }1519 CollectionMode::ReFungible(_) => {1520 <ReFungibleItemList<T>>::get(collection_id, item_id)1521 .owner1522 .iter()1523 .any(|i| i.owner == subject)1524 }1525 CollectionMode::Invalid => false,1526 }1527 }15281529 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1530 let mes = "Address is not in white list";1531 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1532 let wl = <WhiteList<T>>::get(collection_id);1533 ensure!(wl.contains(address), mes);15341535 Ok(())1536 }15371538 fn transfer_fungible(1539 collection_id: u64,1540 item_id: u64,1541 value: u64,1542 owner: T::AccountId,1543 new_owner: T::AccountId,1544 ) -> DispatchResult {1545 ensure!(1546 <FungibleItemList<T>>::contains_key(collection_id, item_id),1547 "Item not exists"1548 );15491550 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1551 let amount = full_item.value;15521553 ensure!(amount >= value.into(), "Item balance not enouth");15541555 // update balance1556 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1557 .checked_sub(value)1558 .unwrap();1559 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15601561 let mut new_owner_account_id = 0;1562 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1563 if new_owner_items.len() > 0 {1564 new_owner_account_id = new_owner_items[0];1565 }15661567 let val64 = value.into();15681569 // transfer1570 if amount == val64 && new_owner_account_id == 0 {1571 // change owner1572 // new owner do not have account1573 let mut new_full_item = full_item.clone();1574 new_full_item.owner = new_owner.clone();1575 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15761577 // update balance1578 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1579 .checked_add(value)1580 .unwrap();1581 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15821583 // update index collection1584 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1585 } else {1586 let mut new_full_item = full_item.clone();1587 new_full_item.value -= val64;15881589 // separate amount1590 if new_owner_account_id > 0 {1591 // new owner has account1592 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1593 item.value += val64;15941595 // update balance1596 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1597 .checked_add(value)1598 .unwrap();1599 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16001601 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1602 } else {1603 // new owner do not have account1604 let item = FungibleItemType {1605 collection: collection_id,1606 owner: new_owner.clone(),1607 value: val64,1608 };16091610 Self::add_fungible_item(item)?;1611 }16121613 if amount == val64 {1614 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16151616 // remove approve list1617 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1618 <FungibleItemList<T>>::remove(collection_id, item_id);1619 }16201621 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1622 }16231624 Ok(())1625 }16261627 fn transfer_refungible(1628 collection_id: u64,1629 item_id: u64,1630 value: u64,1631 owner: T::AccountId,1632 new_owner: T::AccountId,1633 ) -> DispatchResult {1634 ensure!(1635 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1636 "Item not exists"1637 );16381639 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1640 let item = full_item1641 .owner1642 .iter()1643 .filter(|i| i.owner == owner)1644 .next()1645 .unwrap();1646 let amount = item.fraction;16471648 ensure!(amount >= value.into(), "Item balance not enouth");16491650 // update balance1651 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1652 .checked_sub(value)1653 .unwrap();1654 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16551656 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1657 .checked_add(value)1658 .unwrap();1659 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16601661 let old_owner = item.owner.clone();1662 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1663 let val64 = value.into();16641665 // transfer1666 if amount == val64 && !new_owner_has_account {1667 // change owner1668 // new owner do not have account1669 let mut new_full_item = full_item.clone();1670 new_full_item1671 .owner1672 .iter_mut()1673 .find(|i| i.owner == owner)1674 .unwrap()1675 .owner = new_owner.clone();1676 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16771678 // update index collection1679 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1680 } else {1681 let mut new_full_item = full_item.clone();1682 new_full_item1683 .owner1684 .iter_mut()1685 .find(|i| i.owner == owner)1686 .unwrap()1687 .fraction -= val64;16881689 // separate amount1690 if new_owner_has_account {1691 // new owner has account1692 new_full_item1693 .owner1694 .iter_mut()1695 .find(|i| i.owner == new_owner)1696 .unwrap()1697 .fraction += val64;1698 } else {1699 // new owner do not have account1700 new_full_item.owner.push(Ownership {1701 owner: new_owner.clone(),1702 fraction: val64,1703 });1704 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1705 }17061707 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1708 }17091710 Ok(())1711 }17121713 fn transfer_nft(1714 collection_id: u64,1715 item_id: u64,1716 sender: T::AccountId,1717 new_owner: T::AccountId,1718 ) -> DispatchResult {1719 ensure!(1720 <NftItemList<T>>::contains_key(collection_id, item_id),1721 "Item not exists"1722 );17231724 let mut item = <NftItemList<T>>::get(collection_id, item_id);17251726 ensure!(1727 sender == item.owner,1728 "sender parameter and item owner must be equal"1729 );17301731 // update balance1732 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1733 .checked_sub(1)1734 .unwrap();1735 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17361737 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1738 .checked_add(1)1739 .unwrap();1740 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17411742 // change owner1743 let old_owner = item.owner.clone();1744 item.owner = new_owner.clone();1745 <NftItemList<T>>::insert(collection_id, item_id, item);17461747 // update index collection1748 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17491750 // reset approved list1751 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1752 Ok(())1753 }1754 1755 fn item_exists(1756 collection_id: u64,1757 item_id: u64,1758 mode: &CollectionMode1759 ) -> DispatchResult {1760 match mode {1761 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1762 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1763 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1764 _ => ()1765 };1766 1767 Ok(())1768 }17691770 fn set_re_fungible_variable_data(1771 collection_id: u64,1772 item_id: u64,1773 data: Vec<u8>1774 ) -> DispatchResult {1775 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17761777 item.variable_data = data;17781779 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17801781 Ok(())1782 }17831784 fn set_nft_variable_data(1785 collection_id: u64,1786 item_id: u64,1787 data: Vec<u8>1788 ) -> DispatchResult {1789 let mut item = <NftItemList<T>>::get(collection_id, item_id);1790 1791 item.variable_data = data;17921793 <NftItemList<T>>::insert(collection_id, item_id, item);1794 1795 Ok(())1796 }17971798 fn init_collection(item: &CollectionType<T::AccountId>) {1799 // check params1800 assert!(1801 item.decimal_points <= 4,1802 "decimal_points parameter must be lower than 4"1803 );1804 assert!(1805 item.name.len() <= 64,1806 "Collection name can not be longer than 63 char"1807 );1808 assert!(1809 item.name.len() <= 256,1810 "Collection description can not be longer than 255 char"1811 );1812 assert!(1813 item.token_prefix.len() <= 16,1814 "Token prefix can not be longer than 15 char"1815 );18161817 // Generate next collection ID1818 let next_id = CreatedCollectionCount::get()1819 .checked_add(1)1820 .expect("collection id error");18211822 CreatedCollectionCount::put(next_id);1823 }18241825 fn init_nft_token(item: &NftItemType<T::AccountId>) {1826 let current_index = <ItemListIndex>::get(item.collection)1827 .checked_add(1)1828 .expect("Item list index id error");18291830 let item_owner = item.owner.clone();1831 let collection_id = item.collection.clone();1832 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18331834 <ItemListIndex>::insert(collection_id, current_index);18351836 // Update balance1837 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1838 .checked_add(1)1839 .unwrap();1840 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1841 }18421843 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1844 let current_index = <ItemListIndex>::get(item.collection)1845 .checked_add(1)1846 .expect("Item list index id error");1847 let owner = item.owner.clone();1848 let value = item.value as u64;18491850 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18511852 <ItemListIndex>::insert(item.collection, current_index);18531854 // Update balance1855 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1856 .checked_add(value)1857 .unwrap();1858 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1859 }18601861 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1862 let current_index = <ItemListIndex>::get(item.collection)1863 .checked_add(1)1864 .expect("Item list index id error");18651866 let value = item.owner.first().unwrap().fraction as u64;1867 let owner = item.owner.first().unwrap().owner.clone();18681869 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18701871 <ItemListIndex>::insert(item.collection, current_index);18721873 // Update balance1874 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1875 .checked_add(value)1876 .unwrap();1877 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1878 }18791880 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18811882 // add to account limit1883 if <AccountItemCount<T>>::contains_key(owner.clone()) {18841885 // bound Owned tokens by a single address1886 let count = <AccountItemCount<T>>::get(owner.clone());1887 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18881889 <AccountItemCount<T>>::insert(owner.clone(), 1890 count.checked_add(1).unwrap());1891 }1892 else {1893 <AccountItemCount<T>>::insert(owner.clone(), 1);1894 }18951896 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1897 if list_exists {1898 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1899 let item_contains = list.contains(&item_index.clone());19001901 if !item_contains {1902 list.push(item_index.clone());1903 }19041905 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1906 } else {1907 let mut itm = Vec::new();1908 itm.push(item_index.clone());1909 <AddressTokens<T>>::insert(collection_id, owner, itm);1910 1911 }19121913 Ok(())1914 }19151916 fn remove_token_index(1917 collection_id: u64,1918 item_index: u64,1919 owner: T::AccountId,1920 ) -> DispatchResult {19211922 // update counter1923 <AccountItemCount<T>>::insert(owner.clone(), 1924 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());192519261927 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1928 if list_exists {1929 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1930 let item_contains = list.contains(&item_index.clone());19311932 if item_contains {1933 list.retain(|&item| item != item_index);1934 <AddressTokens<T>>::insert(collection_id, owner, list);1935 }1936 }19371938 Ok(())1939 }19401941 fn move_token_index(1942 collection_id: u64,1943 item_index: u64,1944 old_owner: T::AccountId,1945 new_owner: T::AccountId,1946 ) -> DispatchResult {1947 Self::remove_token_index(collection_id, item_index, old_owner)?;1948 Self::add_token_index(collection_id, item_index, new_owner)?;19491950 Ok(())1951 }1952}19531954////////////////////////////////////////////////////////////////////////////////////////////////////1955// Economic models1956// #region19571958/// Fee multiplier.1959pub type Multiplier = FixedU128;19601961type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1962 <T as system::Trait>::AccountId,1963>>::Balance;1964type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1965 <T as system::Trait>::AccountId,1966>>::NegativeImbalance;19671968/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1969/// in the queue.1970#[derive(Encode, Decode, Clone, Eq, PartialEq)]1971pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1972 #[codec(compact)] BalanceOf<T>1973);19741975impl<T: Trait + Send + Sync> sp_std::fmt::Debug1976 for ChargeTransactionPayment<T>1977{1978 #[cfg(feature = "std")]1979 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1980 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1981 }1982 #[cfg(not(feature = "std"))]1983 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1984 Ok(())1985 }1986}19871988impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1989where1990 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1991 BalanceOf<T>: Send + Sync + FixedPointOperand,1992{1993 /// utility constructor. Used only in client/factory code.1994 pub fn from(fee: BalanceOf<T>) -> Self {1995 Self(fee)1996 }19971998 pub fn traditional_fee(1999 len: usize,2000 info: &DispatchInfoOf<T::Call>,2001 tip: BalanceOf<T>,2002 ) -> BalanceOf<T>2003 where2004 T::Call: Dispatchable<Info = DispatchInfo>,2005 {2006 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2007 }20082009 fn withdraw_fee(2010 &self,2011 who: &T::AccountId,2012 call: &T::Call,2013 info: &DispatchInfoOf<T::Call>,2014 len: usize,2015 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2016 let tip = self.0;20172018 // Set fee based on call type. Creating collection costs 1 Unique.2019 // All other transactions have traditional fees so far2020 // let fee = match call.is_sub_type() {2021 // Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2022 // _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2023 // // _ => <BalanceOf<T>>::from(100)2024 // };2025 let fee = Self::traditional_fee(len, info, tip);20262027 // Determine who is paying transaction fee based on ecnomic model2028 // Parse call to extract collection ID and access collection sponsor2029 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2030 Some(Call::create_item(collection_id, _properties, _owner)) => {2031 <Collection<T>>::get(collection_id).sponsor2032 }2033 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2034 let _collection_mode = <Collection<T>>::get(collection_id).mode;20352036 // sponsor timeout2037 let sponsor_transfer = match _collection_mode {2038 CollectionMode::NFT => {2039 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2040 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2041 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2042 if block_number >= limit_time {2043 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2044 true2045 }2046 else {2047 false2048 }2049 }2050 CollectionMode::Fungible(_) => {2051 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2052 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2053 if basket.iter().any(|i| i.address == _new_owner.clone())2054 {2055 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2056 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2057 if block_number >= limit_time {2058 basket.retain(|x| x.address == item.address);2059 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2060 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2061 true2062 }2063 else {2064 false2065 }2066 }2067 else {2068 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2069 true2070 }2071 }2072 CollectionMode::ReFungible(_) => {2073 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2074 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2075 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2076 if block_number >= limit_time {2077 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2078 true2079 } else {2080 false2081 }2082 }2083 _ => {2084 false2085 },2086 };20872088 if !sponsor_transfer {2089 T::AccountId::default()2090 } else {2091 <Collection<T>>::get(collection_id).sponsor2092 }2093 }20942095 _ => T::AccountId::default(),2096 };20972098 // Sponsor smart contracts2099 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21002101 // On instantiation: set the contract owner2102 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21032104 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2105 code_hash,2106 &data,2107 &who,2108 );2109 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21102111 T::AccountId::default()2112 },21132114 // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2115 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21162117 let mut sp = T::AccountId::default();2118 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2119 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2120 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2121 sp = called_contract;2122 }2123 }21242125 sp2126 },21272128 _ => sponsor,2129 };21302131 let mut who_pays_fee: T::AccountId = sponsor.clone();2132 if sponsor == T::AccountId::default() {2133 who_pays_fee = who.clone();2134 }21352136 // Only mess with balances if fee is not zero.2137 if fee.is_zero() {2138 return Ok((fee, None));2139 }21402141 match <T as transaction_payment::Trait>::Currency::withdraw(2142 &who_pays_fee,2143 fee,2144 if tip.is_zero() {2145 WithdrawReason::TransactionPayment.into()2146 } else {2147 WithdrawReason::TransactionPayment | WithdrawReason::Tip2148 },2149 ExistenceRequirement::KeepAlive,2150 ) {2151 Ok(imbalance) => Ok((fee, Some(imbalance))),2152 Err(_) => Err(InvalidTransaction::Payment.into()),2153 }2154 }2155}215621572158impl<T: Trait + Send + Sync> SignedExtension2159 for ChargeTransactionPayment<T>2160where2161 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2162 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2163{2164 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2165 type AccountId = T::AccountId;2166 type Call = T::Call;2167 type AdditionalSigned = ();2168 type Pre = (2169 BalanceOf<T>,2170 Self::AccountId,2171 Option<NegativeImbalanceOf<T>>,2172 BalanceOf<T>,2173 );2174 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2175 Ok(())2176 }21772178 fn validate(2179 &self,2180 _who: &Self::AccountId,2181 _call: &Self::Call,2182 _info: &DispatchInfoOf<Self::Call>,2183 _len: usize,2184 ) -> TransactionValidity {2185 Ok(ValidTransaction::default())2186 }21872188 fn pre_dispatch(2189 self,2190 who: &Self::AccountId,2191 call: &Self::Call,2192 info: &DispatchInfoOf<Self::Call>,2193 len: usize,2194 ) -> Result<Self::Pre, TransactionValidityError> {2195 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2196 Ok((self.0, who.clone(), imbalance, fee))2197 }21982199 fn post_dispatch(2200 pre: Self::Pre,2201 info: &DispatchInfoOf<Self::Call>,2202 post_info: &PostDispatchInfoOf<Self::Call>,2203 len: usize,2204 _result: &DispatchResult,2205 ) -> Result<(), TransactionValidityError> {2206 let (tip, who, imbalance, fee) = pre;2207 if let Some(payed) = imbalance {2208 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2209 len as u32, info, post_info, tip,2210 );2211 let refund = fee.saturating_sub(actual_fee);2212 let actual_payment =2213 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2214 &who, refund,2215 ) {2216 Ok(refund_imbalance) => {2217 // The refund cannot be larger than the up front payed max weight.2218 // `PostDispatchInfo::calc_unspent` guards against such a case.2219 match payed.offset(refund_imbalance) {2220 Ok(actual_payment) => actual_payment,2221 Err(_) => return Err(InvalidTransaction::Payment.into()),2222 }2223 }2224 // We do not recreate the account using the refund. The up front payment2225 // is gone in that case.2226 Err(_) => payed,2227 };2228 let imbalances = actual_payment.split(tip);2229 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2230 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2231 );2232 }2233 Ok(())2234 }2235}22362237// #endregion22382239pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,21 +1,16 @@
// Tests to be written here
use crate::mock::*;
-use crate::{AccessMode, ApprovePermissions, CollectionMode, Ownership, ChainLimits};
+use crate::{AccessMode, ApprovePermissions, CollectionMode, Ownership, ChainLimits, CreateItemData, CreateNftData, CreateFungibleData, CreateReFungibleData};
use frame_support::{assert_noop, assert_ok};
use frame_system::{ RawOrigin };
-// Use cases tests region
-// #region
-#[test]
-fn create_nft_item() {
- new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+fn default_collection_numbers_limit() -> u64 {
+ 10
+}
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
+fn default_limits() {
+ assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
+ collection_numbers_limit: default_collection_numbers_limit(),
account_token_ownership_limit: 10,
collections_admins_limit: 5,
custom_data_limit: 2048,
@@ -23,67 +18,93 @@
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+}
+
+fn default_nft_data() -> CreateNftData {
+ CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }
+}
+
+fn default_fungible_data () -> CreateFungibleData {
+ CreateFungibleData { }
+}
+
+fn default_re_fungible_data () -> CreateReFungibleData {
+ CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] }
+}
+
+fn create_test_collection_for_owner(mode: &CollectionMode, owner: u64, id: u64) -> u64 {
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
+ let origin1 = Origin::signed(owner);
+ assert_ok!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
col_desc1.clone(),
token_prefix1.clone(),
- mode
+ mode.clone()
));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
+ let saved_col_name: Vec<u16> = "Test1\0\0".encode_utf16().collect::<Vec<u16>>();
+ let saved_description: Vec<u16> = "TestDescription1\0\0".encode_utf16().collect::<Vec<u16>>();
+ let saved_prefix: Vec<u8> = b"token_prefix1\0\0".to_vec();
+ assert_eq!(TemplateModule::collection(id).owner, owner);
+ assert_eq!(TemplateModule::collection(id).name, saved_col_name);
+ assert_eq!(TemplateModule::collection(id).mode, *mode);
+ assert_eq!(TemplateModule::collection(id).description, saved_description);
+ assert_eq!(TemplateModule::collection(id).token_prefix, saved_prefix);
+ id
+}
+
+fn create_test_collection(mode: &CollectionMode, id: u64) -> u64 {
+ create_test_collection_for_owner(&mode, 1, id)
+}
+
+fn create_test_item(collection_id: u64, data: &CreateItemData) {
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::create_item(
origin1.clone(),
+ collection_id,
1,
- [1, 2, 3].to_vec(),
- 1
+ data.clone()
));
- assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
+
+}
+
+// Use cases tests region
+// #region
+#[test]
+fn create_nft_item() {
+ new_test_ext().execute_with(|| {
+ default_limits();
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ assert_eq!(TemplateModule::nft_item_id(collection_id, 1).const_data, data.const_data);
+ assert_eq!(TemplateModule::nft_item_id(collection_id, 1).variable_data, data.variable_data);
});
}
#[test]
fn create_refungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
+ default_limits();
+ let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.clone().into());
+ assert_eq!(
+ TemplateModule::refungible_item_id(collection_id, 1).const_data,
+ data.const_data
+ );
assert_eq!(
- TemplateModule::refungible_item_id(1, 1).data,
- [1, 2, 3].to_vec()
+ TemplateModule::refungible_item_id(collection_id, 1).variable_data,
+ data.variable_data
);
assert_eq!(
- TemplateModule::refungible_item_id(1, 1).owner[0],
+ TemplateModule::refungible_item_id(collection_id, 1).owner[0],
Ownership {
owner: 1,
fraction: 1000
@@ -95,38 +116,14 @@
#[test]
fn create_fungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [].to_vec(),
- 1
- ));
- assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
+ assert_eq!(TemplateModule::fungible_item_id(collection_id, 1).owner, 1);
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
});
@@ -135,38 +132,16 @@
#[test]
fn transfer_fungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [].to_vec(),
- 1
- ));
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
+
assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -203,44 +178,25 @@
#[test]
fn transfer_refungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.clone().into());
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
assert_eq!(
- TemplateModule::refungible_item_id(1, 1).data,
- [1, 2, 3].to_vec()
+ TemplateModule::refungible_item_id(collection_id, 1).const_data,
+ data.const_data
+ );
+ assert_eq!(
+ TemplateModule::refungible_item_id(collection_id, 1).variable_data,
+ data.variable_data
);
assert_eq!(
- TemplateModule::refungible_item_id(1, 1).owner[0],
+ TemplateModule::refungible_item_id(collection_id, 1).owner[0],
Ownership {
owner: 1,
fraction: 1000
@@ -310,41 +266,16 @@
#[test]
fn transfer_nft_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
- assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
+ let origin1 = Origin::signed(1);
// default scenario
assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1000));
assert_eq!(TemplateModule::nft_item_id(1, 1).owner, 2);
@@ -358,39 +289,16 @@
#[test]
fn nft_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
- assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -429,39 +337,17 @@
#[test]
fn nft_approve_and_transfer_from_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
- assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+
+ assert_eq!(TemplateModule::nft_item_id(1, 1).const_data, data.const_data);
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -507,49 +393,16 @@
#[test]
fn refungible_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
+
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
- assert_eq!(
- TemplateModule::refungible_item_id(1, 1).data,
- [1, 2, 3].to_vec()
- );
- assert_eq!(
- TemplateModule::refungible_item_id(1, 1).owner[0],
- Ownership {
- owner: 1,
- fraction: 1000
- }
- );
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.into());
+
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -607,39 +460,16 @@
#[test]
fn fungible_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [].to_vec(),
- 1
- ));
- assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -713,103 +543,44 @@
#[test]
fn change_collection_owner() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::change_collection_owner(
origin1.clone(),
- 1,
+ collection_id,
2
));
- assert_eq!(TemplateModule::collection(1).owner, 2);
+ assert_eq!(TemplateModule::collection(collection_id).owner, 2);
});
}
#[test]
fn destroy_collection() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));
});
}
#[test]
fn burn_nft_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
-
- assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec());
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
// check balance (collection with id = 1, user id = 1)
assert_eq!(TemplateModule::balance_count(1, 1), 1);
@@ -828,36 +599,15 @@
#[test]
fn burn_fungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [].to_vec(),
- 1
- ));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
+
+ let data = default_fungible_data();
+ create_test_item(collection_id, &data.into());
// check balance (collection with id = 1, user id = 1)
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
@@ -876,56 +626,28 @@
#[test]
fn burn_refungible_item() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(200, 3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::ReFungible(3), 1);
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
true
));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
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(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+
+ let data = default_re_fungible_data();
+ create_test_item(collection_id, &data.into());
- assert_eq!(
- TemplateModule::refungible_item_id(1, 1).data,
- [1, 2, 3].to_vec()
- );
-
// check balance (collection with id = 1, user id = 2)
assert_eq!(TemplateModule::balance_count(1, 1), 1000);
@@ -943,110 +665,38 @@
#[test]
fn add_collection_admin() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
+ create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
+ create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
+
let origin1 = Origin::signed(1);
- let origin2 = Origin::signed(2);
- let origin3 = Origin::signed(3);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin2.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin3.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
-
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_eq!(TemplateModule::collection(2).owner, 2);
- assert_eq!(TemplateModule::collection(3).owner, 3);
// collection admin
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 2));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 3));
- assert_eq!(TemplateModule::admin_list_collection(1).contains(&2), true);
- assert_eq!(TemplateModule::admin_list_collection(1).contains(&3), true);
+ assert_eq!(TemplateModule::admin_list_collection(collection1_id).contains(&2), true);
+ assert_eq!(TemplateModule::admin_list_collection(collection1_id).contains(&3), true);
});
}
#[test]
fn remove_collection_admin() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
+ create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
+ create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- let origin3 = Origin::signed(3);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin2.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin3.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_eq!(TemplateModule::collection(2).owner, 2);
- assert_eq!(TemplateModule::collection(3).owner, 3);
-
// collection admin
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 2));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection1_id, 3));
assert_eq!(TemplateModule::admin_list_collection(1).contains(&2), true);
assert_eq!(TemplateModule::admin_list_collection(1).contains(&3), true);
@@ -1064,125 +714,48 @@
#[test]
fn balance_of() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let nft_mode: CollectionMode = CollectionMode::NFT(2000);
- let furg_mode: CollectionMode = CollectionMode::Fungible(3);
- let refung_mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
-
- let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- nft_mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- furg_mode.clone()
- ));
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- refung_mode.clone()
- ));
-
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_eq!(TemplateModule::collection(2).owner, 1);
- assert_eq!(TemplateModule::collection(3).owner, 1);
-
+ default_limits();
+
+ let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2);
+ let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible(3), 3);
+
// check balance before
- assert_eq!(TemplateModule::balance_count(1, 1), 0);
- assert_eq!(TemplateModule::balance_count(2, 1), 0);
- assert_eq!(TemplateModule::balance_count(3, 1), 0);
-
- // create item
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 1, 1].to_vec(),
- 1
- ));
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 2,
- [].to_vec(),
- 1
- ));
+ assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 0);
+ assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 0);
+ assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 0);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 3,
- [1, 1, 1].to_vec(),
- 1
- ));
+ let nft_data = default_nft_data();
+ create_test_item(nft_collection_id, &nft_data.into());
+
+ let fungible_data = default_fungible_data();
+ create_test_item(fungible_collection_id, &fungible_data.into());
+
+ let re_fungible_data = default_re_fungible_data();
+ create_test_item(re_fungible_collection_id, &re_fungible_data.into());
// check balance (collection with id = 1, user id = 1)
- assert_eq!(TemplateModule::balance_count(1, 1), 1);
- assert_eq!(TemplateModule::balance_count(2, 1), 1000);
- assert_eq!(TemplateModule::balance_count(3, 1), 1000);
- assert_eq!(TemplateModule::nft_item_id(1, 1).owner, 1);
- assert_eq!(TemplateModule::fungible_item_id(2, 1).owner, 1);
- assert_eq!(TemplateModule::refungible_item_id(3, 1).owner[0].owner, 1);
+ assert_eq!(TemplateModule::balance_count(nft_collection_id, 1), 1);
+ assert_eq!(TemplateModule::balance_count(fungible_collection_id, 1), 1000);
+ assert_eq!(TemplateModule::balance_count(re_fungible_collection_id, 1), 1000);
+ assert_eq!(TemplateModule::nft_item_id(nft_collection_id, 1).owner, 1);
+ assert_eq!(TemplateModule::fungible_item_id(fungible_collection_id, 1).owner, 1);
+ assert_eq!(TemplateModule::refungible_item_id(re_fungible_collection_id, 1).owner[0].owner, 1);
});
}
#[test]
fn approve() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let nft_mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- nft_mode.clone()
- ));
-
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- // create item
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 1, 1].to_vec(),
- 1
- ));
-
+
// approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
@@ -1192,41 +765,15 @@
#[test]
fn transfer_from() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- // create item
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 1, 1].to_vec(),
- 1
- ));
-
// approve
assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1));
assert_eq!(TemplateModule::approved(1, (1, 1))[0].approved, 2);
@@ -1268,99 +815,41 @@
#[test]
fn owner_can_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
- assert_eq!(TemplateModule::white_list(1)[0], 2);
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
+ assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
});
}
#[test]
fn admin_can_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), 1, 3));
- assert_eq!(TemplateModule::white_list(1)[0], 3);
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3));
+ assert_eq!(TemplateModule::white_list(collection_id)[0], 3);
});
}
#[test]
fn nonprivileged_user_cannot_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
- let origin1 = Origin::signed(1);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin2 = Origin::signed(2);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
assert_noop!(
- TemplateModule::add_to_white_list(origin2.clone(), 1, 3),
+ TemplateModule::add_to_white_list(origin2.clone(), collection_id, 3),
"You do not have permissions to modify this collection"
);
});
@@ -1369,17 +858,9 @@
#[test]
fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
new_test_ext().execute_with(|| {
+ default_limits();
+
let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
assert_noop!(
TemplateModule::add_to_white_list(origin1.clone(), 1, 2),
@@ -1391,33 +872,14 @@
#[test]
fn nobody_can_add_address_to_white_list_of_deleted_collection() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
- let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));
assert_noop!(
- TemplateModule::add_to_white_list(origin1.clone(), 1, 2),
+ TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2),
"This collection does not exist"
);
});
@@ -1427,165 +889,80 @@
#[test]
fn address_is_already_added_to_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
- assert_eq!(TemplateModule::white_list(1)[0], 2);
- assert_eq!(TemplateModule::white_list(1).len(), 1);
+
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
+ assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
+ assert_eq!(TemplateModule::white_list(collection_id).len(), 1);
});
}
#[test]
fn owner_can_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
- let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
- 1,
+ collection_id,
2
));
- assert_eq!(TemplateModule::white_list(1).len(), 0);
+ assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
});
}
#[test]
fn admin_can_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 3));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 3));
assert_ok!(TemplateModule::remove_from_white_list(
origin2.clone(),
- 1,
+ collection_id,
3
));
- assert_eq!(TemplateModule::white_list(1).len(), 0);
+ assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
});
}
#[test]
fn nonprivileged_user_cannot_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_noop!(
- TemplateModule::remove_from_white_list(origin2.clone(), 1, 2),
+ TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
"You do not have permissions to modify this collection"
);
- assert_eq!(TemplateModule::white_list(1)[0], 2);
+ assert_eq!(TemplateModule::white_list(collection_id)[0], 2);
});
}
#[test]
fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
new_test_ext().execute_with(|| {
+ default_limits();
let origin1 = Origin::signed(1);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
assert_noop!(
TemplateModule::remove_from_white_list(origin1.clone(), 1, 2),
@@ -1597,38 +974,19 @@
#[test]
fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
+ assert_ok!(TemplateModule::destroy_collection(origin1.clone(), collection_id));
assert_noop!(
- TemplateModule::remove_from_white_list(origin2.clone(), 1, 2),
+ TemplateModule::remove_from_white_list(origin2.clone(), collection_id, 2),
"This collection does not exist"
);
- assert_eq!(TemplateModule::white_list(1).len(), 0);
+ assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
});
}
@@ -1636,42 +994,23 @@
#[test]
fn address_is_already_removed_from_white_list() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
- 1,
+ collection_id,
2
));
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
- 1,
+ collection_id,
2
));
- assert_eq!(TemplateModule::white_list(1).len(), 0);
+ assert_eq!(TemplateModule::white_list(collection_id).len(), 0);
});
}
@@ -1679,44 +1018,21 @@
#[test]
fn white_list_test_1() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
-
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_noop!(
TemplateModule::transfer(origin1.clone(), 3, 1, 1, 1),
@@ -1728,41 +1044,17 @@
#[test]
fn white_list_test_2() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
@@ -1789,41 +1081,18 @@
#[test]
fn white_list_test_3() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 1));
@@ -1838,45 +1107,22 @@
#[test]
fn white_list_test_4() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
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(), collection_id, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
@@ -1884,7 +1130,7 @@
assert_ok!(TemplateModule::remove_from_white_list(
origin1.clone(),
- 1,
+ collection_id,
2
));
@@ -1899,41 +1145,18 @@
#[test]
fn white_list_test_5() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_noop!(
@@ -1947,32 +1170,14 @@
#[test]
fn white_list_test_6() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
@@ -1989,45 +1194,22 @@
#[test]
fn white_list_test_7() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
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(), collection_id, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::transfer(origin1.clone(), 2, 1, 1, 1));
});
@@ -2036,45 +1218,22 @@
#[test]
fn white_list_test_8() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_eq!(TemplateModule::collection(1).owner, 1);
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
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(), collection_id, 1));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
// do approve
assert_ok!(TemplateModule::approve(origin1.clone(), 1, 1, 1));
@@ -2095,47 +1254,24 @@
#[test]
fn white_list_test_9() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
false
));
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
});
}
@@ -2143,49 +1279,31 @@
#[test]
fn white_list_test_10() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
false
));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::create_item(
origin2.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 2
+ collection_id,
+ 2,
+ default_nft_data().into()
));
});
}
@@ -2194,46 +1312,28 @@
#[test]
fn white_list_test_11() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
false
));
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_noop!(
- TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
- "Public minting is not allowed for this collection"
+ TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
+ "Public minting is not allowed for this collection."
);
});
}
@@ -2242,45 +1342,27 @@
#[test]
fn white_list_test_12() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
false
));
assert_noop!(
- TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
- "Public minting is not allowed for this collection"
+ TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
+ "Public minting is not allowed for this collection."
);
});
}
@@ -2289,47 +1371,25 @@
#[test]
fn white_list_test_13() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
true
));
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.into());
});
}
@@ -2337,49 +1397,31 @@
#[test]
fn white_list_test_14() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
true
));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::create_item(
origin2.clone(),
1,
- [1, 2, 3].to_vec(),
- 2
+ 2,
+ default_nft_data().into()
));
});
}
@@ -2388,44 +1430,26 @@
#[test]
fn white_list_test_15() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
true
));
assert_noop!(
- TemplateModule::create_item(origin2.clone(), 1, [1, 2, 3].to_vec(), 2),
+ TemplateModule::create_item(origin2.clone(), 1, 2, default_nft_data().into()),
"Address is not in white list"
);
});
@@ -2435,48 +1459,30 @@
#[test]
fn white_list_test_16() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
assert_ok!(TemplateModule::set_public_access_mode(
origin1.clone(),
- 1,
+ collection_id,
AccessMode::WhiteList
));
assert_ok!(TemplateModule::set_mint_permission(
origin1.clone(),
- 1,
+ collection_id,
true
));
- assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), 1, 2));
+ assert_ok!(TemplateModule::add_to_white_list(origin1.clone(), collection_id, 2));
assert_ok!(TemplateModule::create_item(
origin2.clone(),
1,
- [1, 2, 3].to_vec(),
- 2
+ 2,
+ default_nft_data().into()
));
});
}
@@ -2485,29 +1491,9 @@
#[test]
fn total_number_collections_bound() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
-
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ default_limits();
+
+ create_test_collection(&CollectionMode::NFT, 1);
});
}
@@ -2515,41 +1501,25 @@
#[test]
fn total_number_collections_bound_neg() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
let origin1 = Origin::signed(1);
-
- for _ in 0..10 {
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode.clone()
- ));
+ for i in 0..default_collection_numbers_limit() {
+ create_test_collection(&CollectionMode::NFT, i + 1);
}
+ let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+ let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+ let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
+
// 11-th collection in chain. Expects error
assert_noop!(TemplateModule::create_collection(
origin1.clone(),
col_name1.clone(),
col_desc1.clone(),
token_prefix1.clone(),
- mode.clone()
+ CollectionMode::NFT
), "Total collections bound exceeded");
});
}
@@ -2558,43 +1528,13 @@
#[test]
fn owned_tokens_bound() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
- assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- }));
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
- let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ create_test_item(collection_id, &data.into());
});
}
@@ -2602,11 +1542,6 @@
#[test]
fn owned_tokens_bound_neg() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: 10,
account_token_ownership_limit: 1,
@@ -2616,28 +1551,18 @@
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::create_item(
- origin1.clone(),
- 1,
- [1, 2, 3].to_vec(),
- 1
- ));
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
assert_noop!(TemplateModule::create_item(
origin1.clone(),
1,
- [1, 2, 3].to_vec(),
- 1
+ 1,
+ data.into()
), "Owned tokens by a single address bound exceeded");
});
}
@@ -2646,11 +1571,6 @@
#[test]
fn collection_admins_bound() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: 10,
account_token_ownership_limit: 10,
@@ -2660,18 +1580,13 @@
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
-
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3));
+
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3));
});
}
@@ -2679,11 +1594,6 @@
#[test]
fn collection_admins_bound_neg() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
-
assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: 10,
account_token_ownership_limit: 1,
@@ -2693,78 +1603,167 @@
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
- origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
- assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2));
- assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), 1, 3), "Number of collection admins bound exceeded");
+ assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 2));
+ assert_noop!(TemplateModule::add_collection_admin(origin1.clone(), collection_id, 3), "Number of collection admins bound exceeded");
});
}
-// Custom data size. Positive test
+// NFT custom data size. Negative test const_data.
#[test]
-fn custom_data_size_bound() {
+fn custom_data_size_nft_const_data_bound_neg() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
+ collection_numbers_limit: 10,
+ account_token_ownership_limit: 10,
+ collections_admins_limit: 5,
+ custom_data_limit: 2,
+ nft_sponsor_transfer_timeout: 15,
+ fungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ }));
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ let origin1 = Origin::signed(1);
+ let too_big_const_data = CreateItemData::NFT(CreateNftData{
+ const_data: vec![1, 2, 3, 4],
+ variable_data: vec![]
+ });
+
+ assert_noop!(TemplateModule::create_item(
+ origin1.clone(),
+ collection_id,
+ 1,
+ too_big_const_data
+ ), "const_data exceeded data limit.");
+ });
+}
+
+// NFT custom data size. Negative test variable_data.
+#[test]
+fn custom_data_size_nft_variable_data_bound_neg() {
+ new_test_ext().execute_with(|| {
assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: 10,
account_token_ownership_limit: 10,
collections_admins_limit: 5,
- custom_data_limit: 2048,
+ custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::create_collection(
+ let too_big_const_data = CreateItemData::NFT(CreateNftData{
+ const_data: vec![],
+ variable_data: vec![1, 2, 3, 4]
+ });
+
+ assert_noop!(TemplateModule::create_item(
origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ));
+ collection_id,
+ 1,
+ too_big_const_data
+ ), "variable_data exceeded data limit.");
});
}
-// Custom data size. Negotive test
+// Re fungible custom data size. Negative test const_data.
#[test]
-fn custom_data_size_bound_neg() {
+fn custom_data_size_re_fungible_const_data_bound_neg() {
new_test_ext().execute_with(|| {
- let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
- let mode: CollectionMode = CollectionMode::NFT(2000);
+ assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
+ collection_numbers_limit: 10,
+ account_token_ownership_limit: 10,
+ collections_admins_limit: 5,
+ custom_data_limit: 2,
+ nft_sponsor_transfer_timeout: 15,
+ fungible_sponsor_transfer_timeout: 15,
+ refungible_sponsor_transfer_timeout: 15,
+ }));
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+ let origin1 = Origin::signed(1);
+ let too_big_const_data = CreateItemData::NFT(CreateNftData{
+ const_data: vec![1, 2, 3, 4],
+ variable_data: vec![]
+ });
+
+ assert_noop!(TemplateModule::create_item(
+ origin1.clone(),
+ collection_id,
+ 1,
+ too_big_const_data
+ ), "const_data exceeded data limit.");
+ });
+}
+
+// Re fungible custom data size. Negative test variable_data.
+#[test]
+fn custom_data_size_re_fungible_variable_data_bound_neg() {
+ new_test_ext().execute_with(|| {
assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
collection_numbers_limit: 10,
account_token_ownership_limit: 10,
collections_admins_limit: 5,
- custom_data_limit: 200,
+ custom_data_limit: 2,
nft_sponsor_transfer_timeout: 15,
fungible_sponsor_transfer_timeout: 15,
refungible_sponsor_transfer_timeout: 15,
}));
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
let origin1 = Origin::signed(1);
- assert_noop!(TemplateModule::create_collection(
+ let too_big_const_data = CreateItemData::NFT(CreateNftData{
+ const_data: vec![],
+ variable_data: vec![1, 2, 3, 4]
+ });
+
+ assert_noop!(TemplateModule::create_item(
origin1.clone(),
- col_name1.clone(),
- col_desc1.clone(),
- token_prefix1.clone(),
- mode
- ), "Custom data size bound exceeded");
+ collection_id,
+ 1,
+ too_big_const_data
+ ), "variable_data exceeded data limit.");
});
}
// #endregion
+
+#[test]
+fn set_const_on_chain_schema() {
+ new_test_ext().execute_with(|| {
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::set_const_on_chain_schema(origin1, collection_id, b"test const on chain schema".to_vec()));
+
+ assert_eq!(TemplateModule::collection(collection_id).const_on_chain_schema, b"test const on chain schema".to_vec());
+ assert_eq!(TemplateModule::collection(collection_id).variable_on_chain_schema, b"".to_vec());
+ });
+}
+
+#[test]
+fn set_variable_on_chain_schema() {
+ new_test_ext().execute_with(|| {
+ default_limits();
+
+ let collection_id = create_test_collection(&CollectionMode::NFT, 1);
+
+ let origin1 = Origin::signed(1);
+ assert_ok!(TemplateModule::set_variable_on_chain_schema(origin1, collection_id, b"test variable on chain schema".to_vec()));
+
+ assert_eq!(TemplateModule::collection(collection_id).const_on_chain_schema, b"".to_vec());
+ assert_eq!(TemplateModule::collection(collection_id).variable_on_chain_schema, b"test variable on chain schema".to_vec());
+ });
+}
\ No newline at end of file
runtime/src/nft_weights.rsdiffbeforeafterboth--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -93,4 +93,19 @@
.saturating_add(DbWeight::get().reads(2 as Weight))
.saturating_add(DbWeight::get().writes(1 as Weight))
}
+ fn set_const_on_chain_schema() -> Weight {
+ (11_100_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_on_chain_schema() -> Weight {
+ (11_100_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_meta_data() -> Weight {
+ (17_500_000 as Weight)
+ .saturating_add(DbWeight::get().reads(2 as Weight))
+ .saturating_add(DbWeight::get().writes(1 as Weight))
+ }
}