git.delta.rocks / unique-network / refs/commits / 537f33b30fa3

difftreelog

Merge branch 'develop' into feature/NFTPAR-268_inregration_test_broken

Greg Zaitsev2020-12-30parents: #31c9ea3 #a9dbb97.patch.diff
in: master

24 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
193 offchain_schema: vec![],193 offchain_schema: vec![],
194 schema_version: SchemaVersion::default(),194 schema_version: SchemaVersion::default(),
195 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),195 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
196 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),196 sponsor_confirmed: true,
197 const_on_chain_schema: vec![],197 const_on_chain_schema: vec![],
198 variable_on_chain_schema: vec![],198 variable_on_chain_schema: vec![],
199 limits: CollectionLimits::default()199 limits: CollectionLimits::default()
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
136 pub offchain_schema: Vec<u8>,136 pub offchain_schema: Vec<u8>,
137 pub schema_version: SchemaVersion,137 pub schema_version: SchemaVersion,
138 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender138 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
139 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship139 pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.
140 pub limits: CollectionLimits, // Collection private restrictions 140 pub limits: CollectionLimits, // Collection private restrictions
141 pub variable_on_chain_schema: Vec<u8>, //141 pub variable_on_chain_schema: Vec<u8>, //
142 pub const_on_chain_schema: Vec<u8>, //142 pub const_on_chain_schema: Vec<u8>, //
145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
147pub struct NftItemType<AccountId> {147pub struct NftItemType<AccountId> {
148 pub collection: CollectionId,
149 pub owner: AccountId,148 pub owner: AccountId,
150 pub const_data: Vec<u8>,149 pub const_data: Vec<u8>,
151 pub variable_data: Vec<u8>,150 pub variable_data: Vec<u8>,
152}151}
153152
154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
156pub struct FungibleItemType<AccountId> {155pub struct FungibleItemType {
157 pub collection: CollectionId,
158 pub owner: AccountId,
159 pub value: u128,156 pub value: u128,
160}157}
161158
162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
164pub struct ReFungibleItemType<AccountId> {161pub struct ReFungibleItemType<AccountId> {
165 pub collection: CollectionId,
166 pub owner: Vec<Ownership<AccountId>>,162 pub owner: Vec<Ownership<AccountId>>,
167 pub const_data: Vec<u8>,163 pub const_data: Vec<u8>,
168 pub variable_data: Vec<u8>,164 pub variable_data: Vec<u8>,
169}165}
170166
171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
173pub struct ApprovePermissions<AccountId> {169// pub struct VestingItem<AccountId, Moment> {
174 pub approved: AccountId,170// pub sender: AccountId,
175 pub amount: u128,171// pub recipient: AccountId,
172// pub collection_id: CollectionId,
173// pub item_id: TokenId,
174// pub amount: u64,
175// pub vesting_date: Moment,
176}176// }
177177
178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
180pub struct VestingItem<AccountId, Moment> {
181 pub sender: AccountId,
182 pub recipient: AccountId,
183 pub collection_id: CollectionId,
184 pub item_id: TokenId,
185 pub amount: u64,
186 pub vesting_date: Moment,
187}
188
189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
191pub struct BasketItem<AccountId, BlockNumber> {
192 pub address: AccountId,
193 pub start_block: BlockNumber,
194}
195
196#[derive(Encode, Decode, Debug, Clone, PartialEq)]178#[derive(Encode, Decode, Debug, Clone, PartialEq)]
197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
198pub struct CollectionLimits {180pub struct CollectionLimits {
270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
272pub struct CreateFungibleData {254pub struct CreateFungibleData {
255 pub value: u128,
273}256}
274257
275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]258#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
424 /// Balance owner per collection map407 /// Balance owner per collection map
425 pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;408 pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;
426409
427 /// second parameter: item id + owner account id410 /// second parameter: item id + owner account id + spender account id
428 pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;411 pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;
429412
430 /// Item collections413 /// Item collections
431 pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;414 pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;
432 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;415 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;
433 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;416 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;
434417
435 /// Index list418 /// Index list
436 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;419 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;
437420
438 /// Tokens transfer baskets421 /// Tokens transfer baskets
439 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;422 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
440 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;423 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;
441 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;424 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;
442425
443 // Contract Sponsorship and Ownership426 // Contract Sponsorship and Ownership
455 <Module<T>>::init_collection(_c);438 <Module<T>>::init_collection(_c);
456 }439 }
457440
458 for (_num, _q, _i) in &config.nft_item_id {441 for (_num, _c, _i) in &config.nft_item_id {
459 <Module<T>>::init_nft_token(_i);442 <Module<T>>::init_nft_token(*_c, _i);
460 }443 }
461444
462 for (_num, _q, _i) in &config.fungible_item_id {445 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {
463 <Module<T>>::init_fungible_token(_i);446 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);
464 }447 }
465448
466 for (_num, _q, _i) in &config.refungible_item_id {449 for (_num, _c, _i) in &config.refungible_item_id {
467 <Module<T>>::init_refungible_token(_i);450 <Module<T>>::init_refungible_token(*_c, _i);
468 }451 }
469 })452 })
470 }453 }
591 offchain_schema: Vec::new(),574 offchain_schema: Vec::new(),
592 schema_version: SchemaVersion::ImageURL,575 schema_version: SchemaVersion::ImageURL,
593 sponsor: T::AccountId::default(),576 sponsor: T::AccountId::default(),
594 unconfirmed_sponsor: T::AccountId::default(),577 sponsor_confirmed: false,
595 variable_on_chain_schema: Vec::new(),578 variable_on_chain_schema: Vec::new(),
596 const_on_chain_schema: Vec::new(),579 const_on_chain_schema: Vec::new(),
597 limits: CollectionLimits::default(),580 limits: CollectionLimits::default(),
622 Self::check_owner_permissions(collection_id, sender)?;605 Self::check_owner_permissions(collection_id, sender)?;
623606
624 <AddressTokens<T>>::remove_prefix(collection_id);607 <AddressTokens<T>>::remove_prefix(collection_id);
625 <ApprovedList<T>>::remove_prefix(collection_id);608 <Allowances<T>>::remove_prefix(collection_id);
626 <Balance<T>>::remove_prefix(collection_id);609 <Balance<T>>::remove_prefix(collection_id);
627 <ItemListIndex>::remove(collection_id);610 <ItemListIndex>::remove(collection_id);
628 <AdminList<T>>::remove(collection_id);611 <AdminList<T>>::remove(collection_id);
850 let mut target_collection = <Collection<T>>::get(collection_id);833 let mut target_collection = <Collection<T>>::get(collection_id);
851 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);834 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);
852835
853 target_collection.unconfirmed_sponsor = new_sponsor;836 target_collection.sponsor = new_sponsor;
837 target_collection.sponsor_confirmed = false;
854 <Collection<T>>::insert(collection_id, target_collection);838 <Collection<T>>::insert(collection_id, target_collection);
855839
856 Ok(())840 Ok(())
870 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);854 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);
871855
872 let mut target_collection = <Collection<T>>::get(collection_id);856 let mut target_collection = <Collection<T>>::get(collection_id);
873 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);857 ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);
874858
875 target_collection.sponsor = target_collection.unconfirmed_sponsor;859 target_collection.sponsor_confirmed = true;
876 target_collection.unconfirmed_sponsor = T::AccountId::default();
877 <Collection<T>>::insert(collection_id, target_collection);860 <Collection<T>>::insert(collection_id, target_collection);
878861
879 Ok(())862 Ok(())
898 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);881 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);
899882
900 target_collection.sponsor = T::AccountId::default();883 target_collection.sponsor = T::AccountId::default();
884 target_collection.sponsor_confirmed = false;
901 <Collection<T>>::insert(collection_id, target_collection);885 <Collection<T>>::insert(collection_id, target_collection);
902886
903 Ok(())887 Ok(())
998 /// 982 ///
999 /// * item_id: ID of NFT to burn.983 /// * item_id: ID of NFT to burn.
1000 #[weight = T::WeightInfo::burn_item()]984 #[weight = T::WeightInfo::burn_item()]
1001 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {985 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {
1002986
1003 let sender = ensure_signed(origin)?;987 let sender = ensure_signed(origin)?;
1004 Self::collection_exists(collection_id)?;988 Self::collection_exists(collection_id)?;
1016 match target_collection.mode1000 match target_collection.mode
1017 {1001 {
1018 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1002 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,
1019 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,1003 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,
1020 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1004 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,
1021 _ => ()1005 _ => ()
1022 };1006 };
1072 match target_collection.mode1056 match target_collection.mode
1073 {1057 {
1074 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1058 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,
1075 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1059 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,
1076 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1060 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,
1077 _ => ()1061 _ => ()
1078 };1062 };
1096 /// 1080 ///
1097 /// * item_id: ID of the item.1081 /// * item_id: ID of the item.
1098 #[weight = T::WeightInfo::approve()]1082 #[weight = T::WeightInfo::approve()]
1099 pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1083 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {
11001084
1101 let sender = ensure_signed(origin)?;1085 let sender = ensure_signed(origin)?;
11021086
11081092
1109 if target_collection.access == AccessMode::WhiteList {1093 if target_collection.access == AccessMode::WhiteList {
1110 Self::check_white_list(collection_id, &sender)?;1094 Self::check_white_list(collection_id, &sender)?;
1111 Self::check_white_list(collection_id, &approved)?;1095 Self::check_white_list(collection_id, &spender)?;
1112 }1096 }
11131097
1114 // amount param stub1098 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));
1115 let amount = 100000000;
1116
1117 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
1118 if list_exists {1099 let mut allowance: u128 = amount;
1119
1120 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));
1121 let item_contains = list.iter().any(|i| i.approved == approved);
1122
1123 if !item_contains {1100 if allowance_exists {
1124 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1101 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));
1125 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);
1126 }
1127 } else {
1128
1129 let mut list = Vec::new();
1130 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });
1131 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);
1132 }1102 }
1103 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);
11331104
1134 Ok(())1105 Ok(())
1135 }1106 }
1159 let sender = ensure_signed(origin)?;1130 let sender = ensure_signed(origin)?;
1160 let mut appoved_transfer = false;1131 let mut appoved_transfer = false;
11611132
1162 // Check approve1133 // Check approval
1163 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1134 let mut approval: u128 = 0;
1135 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {
1164 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1136 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));
1165 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());
1166 if opt_item.is_some()1137 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);
1167 {
1168 appoved_transfer = true;
1169 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);
1170 }1138 appoved_transfer = true;
1171 }1139 }
11721140
1173 let target_collection = <Collection<T>>::get(collection_id);1141 let target_collection = <Collection<T>>::get(collection_id);
11771145
1178 // Transfer permissions check 1146 // Transfer permissions check
1179 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1147 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),
1180 Error::<T>::NoPermission);1148 Error::<T>::NoPermission);
11811149
1182 if target_collection.access == AccessMode::WhiteList {1150 if target_collection.access == AccessMode::WhiteList {
1183 Self::check_white_list(collection_id, &sender)?;1151 Self::check_white_list(collection_id, &sender)?;
1184 Self::check_white_list(collection_id, &recipient)?;1152 Self::check_white_list(collection_id, &recipient)?;
1185 }1153 }
11861154
1187 // remove approve1155 // Reduce approval by transferred amount or remove if remaining approval drops to 0
1188 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1156 if approval - value > 0 {
1157 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);
1189 .into_iter().filter(|i| i.approved != sender.clone()).collect();1158 }
1159 else {
1190 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);1160 <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));
1161 }
11911162
1192
1193 match target_collection.mode1163 match target_collection.mode
1194 {1164 {
1195 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1165 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,
1196 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1166 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?,
1197 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1167 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,
1198 _ => ()1168 _ => ()
1199 };1169 };
1627 {1597 {
1628 CreateItemData::NFT(data) => {1598 CreateItemData::NFT(data) => {
1629 let item = NftItemType {1599 let item = NftItemType {
1630 collection: collection_id,
1631 owner,1600 owner,
1632 const_data: data.const_data,1601 const_data: data.const_data,
1633 variable_data: data.variable_data1602 variable_data: data.variable_data
1634 };1603 };
16351604
1636 Self::add_nft_item(item)?;1605 Self::add_nft_item(collection_id, item)?;
1637 },1606 },
1638 CreateItemData::Fungible(_) => {1607 CreateItemData::Fungible(data) => {
1639 let item = FungibleItemType {1608 Self::add_fungible_item(collection_id, &owner, data.value)?;
1640 collection: collection_id,
1641 owner,
1642 value: (10 as u128).pow(collection.decimal_points as u32)
1643 };
1644
1645 Self::add_fungible_item(item)?;
1646 },1609 },
1647 CreateItemData::ReFungible(data) => {1610 CreateItemData::ReFungible(data) => {
1648 let mut owner_list = Vec::new();1611 let mut owner_list = Vec::new();
1649 let value = (10 as u128).pow(collection.decimal_points as u32);1612 let value = (10 as u128).pow(collection.decimal_points as u32);
1650 owner_list.push(Ownership {owner: owner.clone(), fraction: value});1613 owner_list.push(Ownership {owner: owner.clone(), fraction: value});
16511614
1652 let item = ReFungibleItemType {1615 let item = ReFungibleItemType {
1653 collection: collection_id,
1654 owner: owner_list,1616 owner: owner_list,
1655 const_data: data.const_data,1617 const_data: data.const_data,
1656 variable_data: data.variable_data1618 variable_data: data.variable_data
1657 };1619 };
16581620
1659 Self::add_refungible_item(item)?;1621 Self::add_refungible_item(collection_id, item)?;
1660 }1622 }
1661 };1623 };
16621624
1666 Ok(())1628 Ok(())
1667 }1629 }
16681630
1669 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1631 fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {
1670 let current_index = <ItemListIndex>::get(item.collection)
1671 .checked_add(1)
1672 .ok_or(Error::<T>::NumOverflow)?;
1673 let itemcopy = item.clone();
1674 let owner = item.owner.clone();
16751632
1676 Self::add_token_index(item.collection, current_index, owner.clone())?;1633 // Does new owner already have an account?
1634 let mut balance: u128 = 0;
1635 if <FungibleItemList<T>>::contains_key(collection_id, owner) {
1636 balance = <FungibleItemList<T>>::get(collection_id, owner).value;
1637 }
16771638
1678 <ItemListIndex>::insert(item.collection, current_index);1639 // Mint
1640 let item = FungibleItemType {
1641 value: balance + value
1642 };
1679 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);1643 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);
16801644
1681 // Add current block
1682 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();
1683 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);
1684
1685 // Update balance1645 // Update balance
1686 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1646 let new_balance = <Balance<T>>::get(collection_id, owner)
1687 .checked_add(item.value)1647 .checked_add(value)
1688 .ok_or(Error::<T>::NumOverflow)?;1648 .ok_or(Error::<T>::NumOverflow)?;
1689 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1649 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);
16901650
1691 Ok(())1651 Ok(())
1692 }1652 }
16931653
1694 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1654 fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {
1695 let current_index = <ItemListIndex>::get(item.collection)1655 let current_index = <ItemListIndex>::get(collection_id)
1696 .checked_add(1)1656 .checked_add(1)
1697 .ok_or(Error::<T>::NumOverflow)?;1657 .ok_or(Error::<T>::NumOverflow)?;
1698 let itemcopy = item.clone();1658 let itemcopy = item.clone();
16991659
1700 let value = item.owner.first().unwrap().fraction;1660 let value = item.owner.first().unwrap().fraction;
1701 let owner = item.owner.first().unwrap().owner.clone();1661 let owner = item.owner.first().unwrap().owner.clone();
17021662
1703 Self::add_token_index(item.collection, current_index, owner.clone())?;1663 Self::add_token_index(collection_id, current_index, owner.clone())?;
17041664
1705 <ItemListIndex>::insert(item.collection, current_index);1665 <ItemListIndex>::insert(collection_id, current_index);
1706 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);1666 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);
17071667
1708 // Add current block
1709 let block_number: T::BlockNumber = 0.into();
1710 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);
1711
1712 // Update balance1668 // Update balance
1713 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1669 let new_balance = <Balance<T>>::get(collection_id, owner.clone())
1714 .checked_add(value)1670 .checked_add(value)
1715 .ok_or(Error::<T>::NumOverflow)?;1671 .ok_or(Error::<T>::NumOverflow)?;
1716 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1672 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);
17171673
1718 Ok(())1674 Ok(())
1719 }1675 }
17201676
1721 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1677 fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {
1722 let current_index = <ItemListIndex>::get(item.collection)1678 let current_index = <ItemListIndex>::get(collection_id)
1723 .checked_add(1)1679 .checked_add(1)
1724 .ok_or(Error::<T>::NumOverflow)?;1680 .ok_or(Error::<T>::NumOverflow)?;
17251681
1726 let item_owner = item.owner.clone();1682 let item_owner = item.owner.clone();
1727 let collection_id = item.collection.clone();
1728 Self::add_token_index(collection_id, current_index, item.owner.clone())?;1683 Self::add_token_index(collection_id, current_index, item.owner.clone())?;
17291684
1730 <ItemListIndex>::insert(collection_id, current_index);1685 <ItemListIndex>::insert(collection_id, current_index);
1731 <NftItemList<T>>::insert(collection_id, current_index, item);1686 <NftItemList<T>>::insert(collection_id, current_index, item);
17321687
1733 // Add current block
1734 let block_number: T::BlockNumber = 0.into();
1735 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);
1736
1737 // Update balance1688 // Update balance
1738 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1689 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())
1739 .checked_add(1)1690 .checked_add(1)
1761 .unwrap();1712 .unwrap();
1762 Self::remove_token_index(collection_id, item_id, owner.clone())?;1713 Self::remove_token_index(collection_id, item_id, owner.clone())?;
17631714
1764 // remove approve list
1765 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));
1766
1767 // update balance1715 // update balance
1768 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1716 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
1769 .checked_sub(item.fraction)1717 .checked_sub(item.fraction)
1783 let item = <NftItemList<T>>::get(collection_id, item_id);1731 let item = <NftItemList<T>>::get(collection_id, item_id);
1784 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1732 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;
17851733
1786 // remove approve list
1787 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));
1788
1789 // update balance1734 // update balance
1790 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1735 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())
1791 .checked_sub(1)1736 .checked_sub(1)
1796 Ok(())1741 Ok(())
1797 }1742 }
17981743
1799 fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1744 fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {
1800 ensure!(1745 ensure!(
1801 <FungibleItemList<T>>::contains_key(collection_id, item_id),1746 <FungibleItemList<T>>::contains_key(collection_id, owner),
1802 Error::<T>::TokenNotFound1747 Error::<T>::TokenNotFound
1803 );1748 );
1804 let item = <FungibleItemList<T>>::get(collection_id, item_id);1749 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);
1805 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1750 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);
18061751
1807 // remove approve list
1808 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));
1809
1810 // update balance1752 // update balance
1811 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1753 let new_balance = <Balance<T>>::get(collection_id, owner)
1812 .checked_sub(item.value)1754 .checked_sub(value)
1813 .ok_or(Error::<T>::NumOverflow)?;1755 .ok_or(Error::<T>::NumOverflow)?;
1814 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1756 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);
18151757
1816 <FungibleItemList<T>>::remove(collection_id, item_id);1758 if balance.value - value > 0 {
1759 balance.value -= value;
1760 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);
1761 }
1762 else {
1763 <FungibleItemList<T>>::remove(collection_id, owner);
1764 }
18171765
1818 Ok(())1766 Ok(())
1819 }1767 }
1874 <NftItemList<T>>::get(collection_id, item_id).owner == subject1822 <NftItemList<T>>::get(collection_id, item_id).owner == subject
1875 }1823 }
1876 CollectionMode::Fungible(_) => {1824 CollectionMode::Fungible(_) => {
1877 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1825 <FungibleItemList<T>>::contains_key(collection_id, &subject)
1878 }1826 }
1879 CollectionMode::ReFungible(_) => {1827 CollectionMode::ReFungible(_) => {
1880 <ReFungibleItemList<T>>::get(collection_id, item_id)1828 <ReFungibleItemList<T>>::get(collection_id, item_id)
18951843
1896 fn transfer_fungible(1844 fn transfer_fungible(
1897 collection_id: CollectionId,1845 collection_id: CollectionId,
1898 item_id: TokenId,
1899 value: u128,1846 value: u128,
1900 owner: T::AccountId,1847 owner: &T::AccountId,
1901 new_owner: T::AccountId,1848 recipient: &T::AccountId,
1902 ) -> DispatchResult {1849 ) -> DispatchResult {
1903 ensure!(1850 ensure!(
1904 <FungibleItemList<T>>::contains_key(collection_id, item_id),1851 <FungibleItemList<T>>::contains_key(collection_id, owner),
1905 Error::<T>::TokenNotFound1852 Error::<T>::TokenNotFound
1906 );1853 );
19071854
1908 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1855 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);
1909 let amount = full_item.value;1856 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);
19101857
1911 ensure!(amount >= value, Error::<T>::TokenValueTooLow);1858 // Send balance to recipient (updates balanceOf of recipient)
1859 Self::add_fungible_item(collection_id, recipient, value)?;
19121860
1913 // update balance1861 // update balanceOf of sender
1914 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1862 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);
1915 .checked_sub(value)
1916 .ok_or(Error::<T>::NumOverflow)?;
1917 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);
19181863
1919 let mut new_owner_account_id = 0;1864 // Reduce or remove sender
1920 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1865 if balance.value == value {
1921 if new_owner_items.len() > 0 {
1922 new_owner_account_id = new_owner_items[0];1866 <FungibleItemList<T>>::remove(collection_id, owner);
1923 }1867 }
19241868 else {
1925 // transfer1869 balance.value -= value;
1926 if amount == value && new_owner_account_id == 0 {1870 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);
1927 // change owner
1928 // new owner do not have account
1929 let mut new_full_item = full_item.clone();
1930 new_full_item.owner = new_owner.clone();
1931 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
1932
1933 // update balance
1934 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())
1935 .checked_add(value)
1936 .ok_or(Error::<T>::NumOverflow)?;
1937 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);
1938
1939 // update index collection
1940 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;
1941 } else {
1942 let mut new_full_item = full_item.clone();
1943 new_full_item.value -= value;
1944
1945 // separate amount
1946 if new_owner_account_id > 0 {
1947 // new owner has account
1948 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);
1949 item.value += value;
1950
1951 // update balance
1952 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())
1953 .checked_add(value)
1954 .ok_or(Error::<T>::NumOverflow)?;
1955 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);
1956
1957 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);
1958 } else {
1959 // new owner do not have account
1960 let item = FungibleItemType {
1961 collection: collection_id,
1962 owner: new_owner.clone(),
1963 value
1964 };
1965
1966 Self::add_fungible_item(item)?;
1967 }
1968
1969 if amount == value {
1970 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;
1971
1972 // remove approve list
1973 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));
1974 <FungibleItemList<T>>::remove(collection_id, item_id);
1975 }
1976
1977 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
1978 }1871 }
19791872
1980 Ok(())1873 Ok(())
2009 .ok_or(Error::<T>::NumOverflow)?;1902 .ok_or(Error::<T>::NumOverflow)?;
2010 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);1903 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);
20111904
2012 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1905 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())
2013 .checked_add(value)1906 .checked_add(value)
2014 .ok_or(Error::<T>::NumOverflow)?;1907 .ok_or(Error::<T>::NumOverflow)?;
2015 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);1908 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);
20161909
2017 let old_owner = item.owner.clone();1910 let old_owner = item.owner.clone();
2018 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1911 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);
2089 .ok_or(Error::<T>::NumOverflow)?;1982 .ok_or(Error::<T>::NumOverflow)?;
2090 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);1983 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);
20911984
2092 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1985 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())
2093 .checked_add(1)1986 .checked_add(1)
2094 .ok_or(Error::<T>::NumOverflow)?;1987 .ok_or(Error::<T>::NumOverflow)?;
2095 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);1988 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);
20961989
2097 // change owner1990 // change owner
2098 let old_owner = item.owner.clone();1991 let old_owner = item.owner.clone();
2102 // update index collection1995 // update index collection
2103 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1996 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;
21041997
2105 // reset approved list
2106 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));
2107 Ok(())1998 Ok(())
2108 }1999 }
2109 2000
2115 match mode {2006 match mode {
2116 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2007 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
2117 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2008 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
2118 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),
2119 _ => ()2009 _ => ()
2120 };2010 };
2121 2011
2177 CreatedCollectionCount::put(next_id);2067 CreatedCollectionCount::put(next_id);
2178 }2068 }
21792069
2180 fn init_nft_token(item: &NftItemType<T::AccountId>) {2070 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {
2181 let current_index = <ItemListIndex>::get(item.collection)2071 let current_index = <ItemListIndex>::get(collection_id)
2182 .checked_add(1)2072 .checked_add(1)
2183 .unwrap();2073 .unwrap();
21842074
2185 let item_owner = item.owner.clone();2075 let item_owner = item.owner.clone();
2186 let collection_id = item.collection.clone();
2187 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();2076 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();
21882077
2189 <ItemListIndex>::insert(collection_id, current_index);2078 <ItemListIndex>::insert(collection_id, current_index);
2195 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2084 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);
2196 }2085 }
21972086
2198 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2087 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {
2199 let current_index = <ItemListIndex>::get(item.collection)2088 let current_index = <ItemListIndex>::get(collection_id)
2200 .checked_add(1)2089 .checked_add(1)
2201 .unwrap();2090 .unwrap();
2202 let owner = item.owner.clone();
22032091
2204 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2092 Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();
22052093
2206 <ItemListIndex>::insert(item.collection, current_index);2094 <ItemListIndex>::insert(collection_id, current_index);
22072095
2208 // Update balance2096 // Update balance
2209 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2097 let new_balance = <Balance<T>>::get(collection_id, owner)
2210 .checked_add(item.value)2098 .checked_add(item.value)
2211 .unwrap();2099 .unwrap();
2212 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2100 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);
2213 }2101 }
22142102
2215 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2103 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {
2216 let current_index = <ItemListIndex>::get(item.collection)2104 let current_index = <ItemListIndex>::get(collection_id)
2217 .checked_add(1)2105 .checked_add(1)
2218 .unwrap();2106 .unwrap();
22192107
2220 let value = item.owner.first().unwrap().fraction;2108 let value = item.owner.first().unwrap().fraction;
2221 let owner = item.owner.first().unwrap().owner.clone();2109 let owner = item.owner.first().unwrap().owner.clone();
22222110
2223 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2111 Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();
22242112
2225 <ItemListIndex>::insert(item.collection, current_index);2113 <ItemListIndex>::insert(collection_id, current_index);
22262114
2227 // Update balance2115 // Update balance
2228 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2116 let new_balance = <Balance<T>>::get(collection_id, owner.clone())
2229 .checked_add(value)2117 .checked_add(value)
2230 .unwrap();2118 .unwrap();
2231 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2119 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);
2232 }2120 }
22332121
2234 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {2122 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {
2399 // };2287 // };
2400 let fee = Self::traditional_fee(len, info, tip);2288 let fee = Self::traditional_fee(len, info, tip);
24012289
2290 // Only mess with balances if fee is not zero.
2291 if fee.is_zero() {
2292 return Ok((fee, None));
2293 }
2294
2402 // Determine who is paying transaction fee based on ecnomic model2295 // Determine who is paying transaction fee based on ecnomic model
2403 // Parse call to extract collection ID and access collection sponsor2296 // Parse call to extract collection ID and access collection sponsor
2404 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2297 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {
2405 Some(Call::create_item(collection_id, _owner, _properties)) => {2298 Some(Call::create_item(collection_id, _owner, _properties)) => {
24062299
2407 // check free create limit2300 // check free create limit
2408 if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2301 if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&
2302 (<Collection<T>>::get(collection_id).sponsor_confirmed)
2409 {2303 {
2410 <Collection<T>>::get(collection_id).sponsor2304 <Collection<T>>::get(collection_id).sponsor
2411 } else {2305 } else {
2412 T::AccountId::default()2306 T::AccountId::default()
2413 }2307 }
2414 }2308 }
2415 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2309 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {
2416 2310
2417 let _collection_limits = <Collection<T>>::get(collection_id).limits;2311 let mut sponsor_transfer = false;
2418 let _collection_mode = <Collection<T>>::get(collection_id).mode;2312 if <Collection<T>>::get(collection_id).sponsor_confirmed {
24192313
2420 // sponsor timeout2314 let collection_limits = <Collection<T>>::get(collection_id).limits;
2315 let collection_mode = <Collection<T>>::get(collection_id).mode;
2316
2317 // sponsor timeout
2421 let sponsor_transfer = match _collection_mode {2318 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
2319 sponsor_transfer = match collection_mode {
2422 CollectionMode::NFT => {2320 CollectionMode::NFT => {
2321
2322 // get correct limit
2323 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
2324 collection_limits.sponsor_transfer_timeout
2325 } else {
2326 ChainLimit::get().nft_sponsor_transfer_timeout
2327 };
2328
2329 let mut sponsored = true;
2330 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {
2331 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);
2332 let limit_time = last_tx_block + limit.into();
2333 if block_number <= limit_time {
2334 sponsored = false;
2335 }
2336 }
2337 if sponsored {
2338 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);
2339 }
24232340
2424 // get correct limit2341 sponsored
2425 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2426 _collection_limits.sponsor_transfer_timeout
2427 } else {
2428 ChainLimit::get().nft_sponsor_transfer_timeout
2429 };
2430
2431 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);
2432 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
2433 let limit_time = basket + limit.into();
2434 if block_number >= limit_time {
2435 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);
2436 true
2437 }2342 }
2438 else {2343 CollectionMode::Fungible(_) => {
2439 false2344
2440 }
2441 }
2442 CollectionMode::Fungible(_) => {
2443
2444 // get correct limit2345 // get correct limit
2445 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2346 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
2446 _collection_limits.sponsor_transfer_timeout2347 collection_limits.sponsor_transfer_timeout
2447 } else {2348 } else {
2448 ChainLimit::get().fungible_sponsor_transfer_timeout2349 ChainLimit::get().fungible_sponsor_transfer_timeout
2449 };2350 };
2450
2451 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2351
2452 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2352 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
2453 if basket.iter().any(|i| i.address == _new_owner.clone())2353 let mut sponsored = true;
2454 {
2455 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2354 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {
2456 let limit_time = item.start_block + limit.into();2355 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);
2457 if block_number >= limit_time {2356 let limit_time = last_tx_block + limit.into();
2458 basket.retain(|x| x.address == item.address);
2459 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2357 if block_number <= limit_time {
2460 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2358 sponsored = false;
2461 true2359 }
2462 }2360 }
2463 else {2361 if sponsored {
2464 false2362 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);
2465 }2363 }
2364
2365 sponsored
2466 }2366 }
2467 else {2367 CollectionMode::ReFungible(_) => {
2468 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2368
2369 // get correct limit
2370 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
2371 collection_limits.sponsor_transfer_timeout
2372 } else {
2373 ChainLimit::get().refungible_sponsor_transfer_timeout
2374 };
2375
2376 let mut sponsored = true;
2377 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {
2378 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);
2379 let limit_time = last_tx_block + limit.into();
2469 true2380 if block_number <= limit_time {
2470 }2381 sponsored = false;
2382 }
2471 }2383 }
2472 CollectionMode::ReFungible(_) => {2384 if sponsored {
2385 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);
2386 }
24732387
2474 // get correct limit2388 sponsored
2475 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2476 _collection_limits.sponsor_transfer_timeout
2477 } else {
2478 ChainLimit::get().refungible_sponsor_transfer_timeout
2479 };
2480
2481 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);
2482 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;
2483 let limit_time = basket + limit.into();
2484 if block_number >= limit_time {
2485 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);
2486 true
2487 } else {
2488 false
2489 }2389 }
2490 }2390 _ => {
2491 _ => {2391 false
2492 false2392 },
2493 },2393 };
2494 };2394 }
24952395
2496 if !sponsor_transfer {2396 if !sponsor_transfer {
2497 T::AccountId::default()2397 T::AccountId::default()
2568 let mut who_pays_fee: T::AccountId = sponsor.clone();2468 let mut who_pays_fee: T::AccountId = sponsor.clone();
2569 if sponsor == T::AccountId::default() {2469 if sponsor == T::AccountId::default() {
2570 who_pays_fee = who.clone();2470 who_pays_fee = who.clone();
2571 }
2572
2573 // Only mess with balances if fee is not zero.
2574 if fee.is_zero() {
2575 return Ok((fee, None));
2576 }2471 }
25772472
2578 match <T as transaction_payment::Trait>::Currency::withdraw(2473 match <T as transaction_payment::Trait>::Currency::withdraw(
modifiedruntime_types.jsondiffbeforeafterboth
42 "Fraction": "u128"42 "Fraction": "u128"
43 },43 },
44 "FungibleItemType": {44 "FungibleItemType": {
45 "Collection": "CollectionId",
46 "Owner": "AccountId",
47 "Value": "u128"45 "Value": "u128"
48 },46 },
49 "NftItemType": {47 "NftItemType": {
50 "Collection": "CollectionId",
51 "Owner": "AccountId",48 "Owner": "AccountId",
52 "ConstData": "Vec<u8>",49 "ConstData": "Vec<u8>",
53 "VariableData": "Vec<u8>"50 "VariableData": "Vec<u8>"
54 },51 },
55 "ReFungibleItemType": {52 "ReFungibleItemType": {
56 "Collection": "CollectionId",
57 "Owner": "Vec<Ownership<AccountId>>",53 "Owner": "Vec<Ownership<AccountId>>",
58 "ConstData": "Vec<u8>",54 "ConstData": "Vec<u8>",
59 "VariableData": "Vec<u8>"55 "VariableData": "Vec<u8>"
70 "OffchainSchema": "Vec<u8>",66 "OffchainSchema": "Vec<u8>",
71 "SchemaVersion": "SchemaVersion",67 "SchemaVersion": "SchemaVersion",
72 "Sponsor": "AccountId",68 "Sponsor": "AccountId",
73 "UnconfirmedSponsor": "AccountId",69 "SponsorConfirmed": "bool",
74 "Limits": "CollectionLimits",70 "Limits": "CollectionLimits",
75 "VariableOnChainSchema": "Vec<u8>",71 "VariableOnChainSchema": "Vec<u8>",
76 "ConstOnChainSchema": "Vec<u8>"72 "ConstOnChainSchema": "Vec<u8>"
77 },73 },
78 "ApprovePermissions": {
79 "Approved": "AccountId",
80 "Amount": "u128"
81 },
82 "RawData": "Vec<u8>",74 "RawData": "Vec<u8>",
83 "Address": "AccountId",75 "Address": "AccountId",
84 "LookupSource": "AccountId",76 "LookupSource": "AccountId",
88 "variable_data": "Vec<u8>" 80 "variable_data": "Vec<u8>"
89 },81 },
90 "CreateFungibleData": {},82 "CreateFungibleData": {
83 "value": "u128"
84 },
91 "CreateReFungibleData": {85 "CreateReFungibleData": {
92 "const_data": "Vec<u8>",86 "const_data": "Vec<u8>",
107 },101 },
108 "CollectionId": "u32",102 "CollectionId": "u32",
109 "TokenId": "u32",103 "TokenId": "u32",
110 "BasketItem": {
111 "Address": "AccountId",
112 "start_block": "BlockNumber"
113 },
114 "ChainLimits": {104 "ChainLimits": {
115 "collection_numbers_limit": "u32",105 "collection_numbers_limit": "u32",
116 "account_token_ownership_limit": "u32",106 "account_token_ownership_limit": "u32",
addedtests/loadtester-src/.gitignorediffbeforeafterboth

no changes

addedtests/loadtester-src/Cargo.tomldiffbeforeafterboth

no changes

addedtests/loadtester-src/lib.rsdiffbeforeafterboth

no changes

modifiedtests/package.jsondiffbeforeafterboth
16 "typescript": "^3.9.7"16 "typescript": "^3.9.7"
17 },17 },
18 "scripts": {18 "scripts": {
19 "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts"19 "test": "mocha --timeout 9999999 -r ts-node/register ./**/*.test.ts",
20 "load": "mocha --timeout 9999999 -r ts-node/register ./**/*.load.ts"
20 },21 },
21 "author": "",22 "author": "",
22 "license": "Apache 2.0",23 "license": "Apache 2.0",
addedtests/src/change-collection-owner.test.tsdiffbeforeafterboth

no changes

addedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth

no changes

modifiedtests/src/connection.test.tsdiffbeforeafterboth
21 });21 });
2222
23 it('Cannot connect to 255.255.255.255', async () => {23 it('Cannot connect to 255.255.255.255', async () => {
24 const log = console.log;
25 const error = console.error;
24 console.log = function () {};26 console.log = function () {};
25 console.error = function () {};27 console.error = function () {};
2628
31 }, { provider: neverConnectProvider });33 }, { provider: neverConnectProvider });
32 })()).to.be.eventually.rejected;34 })()).to.be.eventually.rejected;
3335
34 delete console.log;36 console.log = log;
35 delete console.error;37 console.error = error;
36 });38 });
37});39});
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
109 const bob = privateKey("//Bob");109 const bob = privateKey("//Bob");
110 110
111 const [contract, deployer] = await deployFlipper(api);111 const [contract, deployer] = await deployFlipper(api);
112 const consoleError = console.error;
113 console.error = (...data: any[]) => {
114 };
115112
116 let expectedFlipValue = await getFlipValue(contract, deployer);113 let expectedFlipValue = await getFlipValue(contract, deployer);
117114
166 const afterWhiteListDisabled = await getFlipValue(contract,deployer);163 const afterWhiteListDisabled = await getFlipValue(contract,deployer);
167 expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`);164 expect(afterWhiteListDisabled).to.be.eq(expectedFlipValue, `Anyone can call contract with disabled whitelist.`);
168165
169 console.error = consoleError;
170 });166 });
171 });167 });
172168
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
6import chai from 'chai'; 6import chai from 'chai';
7import chaiAsPromised from 'chai-as-promised'; 7import chaiAsPromised from 'chai-as-promised';
8import { default as usingApi } from "./substrate/substrate-api"; 8import { default as usingApi } from "./substrate/substrate-api";
9import { createCollectionExpectSuccess, createCollectionExpectFailure } from "./util/helpers"; 9import { createCollectionExpectSuccess, createCollectionExpectFailure, CollectionMode } from "./util/helpers";
10 10
11chai.use(chaiAsPromised); 11chai.use(chaiAsPromised);
12const expect = chai.expect; 12const expect = chai.expect;
13 13
14describe('integration test: ext. createCollection():', () => { 14describe('integration test: ext. createCollection():', () => {
15 it('Create new NFT collection', async () => { 15 it('Create new NFT collection', async () => {
16 await createCollectionExpectSuccess('A', 'B', 'C', 'NFT'); 16 await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: 'NFT'});
17 }); 17 });
18 it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => { 18 it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {
19 await createCollectionExpectSuccess( 19 await createCollectionExpectSuccess({name: 'A'.repeat(64)});
20 'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD',
21 '1', '1', 'NFT');
22 }); 20 });
23 it('Create new NFT collection whith collection_description of maximum length (256 bytes)', async () => { 21 it('Create new NFT collection whith collection_description of maximum length (256 bytes)', async () => {
24 await createCollectionExpectSuccess( 22 await createCollectionExpectSuccess({description: 'A'.repeat(256)});
25 'A',
26 'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJabcdef',
27 '1', 'NFT');
28 }); 23 });
29 it('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async () => { 24 it('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async () => {
30 await createCollectionExpectSuccess( 25 await createCollectionExpectSuccess({tokenPrefix: 'A'.repeat(16)});
31 '1',
32 '1',
33 'ABCDEFGHIJABCDEF', 'NFT');
34 }); 26 });
35 it('Create new Fungible collection', async () => { 27 it('Create new Fungible collection', async () => {
36 await createCollectionExpectSuccess('1', '1', '1', 'Fungible'); 28 await createCollectionExpectSuccess({mode: 'Fungible'});
37 }); 29 });
38 it('Create new ReFungible collection', async () => { 30 it('Create new ReFungible collection', async () => {
39 await createCollectionExpectSuccess('1', '1', '1', 'ReFungible'); 31 await createCollectionExpectSuccess({mode: 'ReFungible'});
40 }); 32 });
41}); 33});
42 34
46 const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString()); 38 const AcollectionCount = parseInt((await api.query.nft.collectionCount()).toString());
47 39
48 const badTransaction = async function () { 40 const badTransaction = async function () {
49 await createCollectionExpectSuccess('1', '1', '1', 'BadMode'); 41 await createCollectionExpectSuccess({mode: 'BadMode' as CollectionMode});
50 }; 42 };
51 expect(badTransaction()).to.be.rejected; 43 expect(badTransaction()).to.be.rejected;
52 44
55 }); 47 });
56 }); 48 });
57 it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => { 49 it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {
58 await createCollectionExpectFailure( 50 await createCollectionExpectFailure({name: 'A'.repeat(65)});
59 'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDE',
60 '1', '1', 'NFT');
61 }); 51 });
62 it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => { 52 it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {
63 await createCollectionExpectFailure('1', 53 await createCollectionExpectFailure({description: 'A'.repeat(257)});
64 'ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJabcdefg',
65 '1', 'NFT');
66 }); 54 });
67 it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => { 55 it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {
68 await createCollectionExpectFailure('1', '1', 56 await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17)});
69 'ABCDEFGHIJABCDEFG',
70 'NFT');
71 }); 57 });
72}); 58});
7359
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
1import { assert } from 'chai';
2import { alicesPublicKey } from './accounts';
3import privateKey from './substrate/privateKey';
4import { default as usingApi } from './substrate/substrate-api'; 1import { default as usingApi } from './substrate/substrate-api';
2import { Keyring } from "@polkadot/api";
5import waitNewBlocks from './substrate/wait-new-blocks'; 3import { IKeyringPair } from "@polkadot/types/types";
6import { 4import {
7 createCollectionExpectSuccess, 5 createCollectionExpectSuccess,
8 createItemExpectSuccess 6 createItemExpectSuccess
9} from './util/helpers'; 7} from './util/helpers';
10 8
9let alice: IKeyringPair;
10
11describe('integration test: ext. createItem():', () => { 11describe('integration test: ext. createItem():', () => {
12 before(async () => {
13 await usingApi(async (api) => {
14 const keyring = new Keyring({ type: 'sr25519' });
15 alice = keyring.addFromUri(`//Alice`);
16 });
17 });
18
12 it('Create new item in NFT collection', async () => { 19 it('Create new item in NFT collection', async () => {
13 const createMode = 'NFT'; 20 const createMode = 'NFT';
14 const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode); 21 const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
15 await createItemExpectSuccess(newCollectionID, createMode, '//Alice'); 22 await createItemExpectSuccess(alice, newCollectionID, createMode);
16 }); 23 });
17 it('Create new item in Fungible collection', async () => { 24 it('Create new item in Fungible collection', async () => {
18 const createMode = 'Fungible'; 25 const createMode = 'Fungible';
19 const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode); 26 const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
20 await createItemExpectSuccess(newCollectionID, createMode, '//Alice'); 27 await createItemExpectSuccess(alice, newCollectionID, createMode);
21 }); 28 });
22 it('Create new item in ReFungible collection', async () => { 29 it('Create new item in ReFungible collection', async () => {
23 const createMode = 'ReFungible'; 30 const createMode = 'ReFungible';
24 const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode); 31 const newCollectionID = await createCollectionExpectSuccess({mode: createMode});
25 await createItemExpectSuccess(newCollectionID, createMode, '//Alice'); 32 await createItemExpectSuccess(alice, newCollectionID, createMode);
26 }); 33 });
27}); 34});
2835
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
1111
12const idCollection = 12;12const idCollection = 12;
1313
14describe('integration test: ext. createMultipleItems():', () => {14describe.skip('integration test: ext. createMultipleItems():', () => {
15 it('Create two NFT tokens in active NFT collection', async () => {15 it('Create two NFT tokens in active NFT collection', async () => {
16 await usingApi(async (api) => {16 await usingApi(async (api) => {
17 const AitemListIndex = await api.query.nft.itemListIndex(idCollection);17 const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
modifiedtests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth
55
6import chai from 'chai';6import chai from 'chai';
7import chaiAsPromised from 'chai-as-promised';7import chaiAsPromised from 'chai-as-promised';
8import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";8import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "./substrate/substrate-api";
9import { alicesPublicKey, bobsPublicKey } from "./accounts";9import { alicesPublicKey, bobsPublicKey } from "./accounts";
10import privateKey from "./substrate/privateKey";10import privateKey from "./substrate/privateKey";
11import { BigNumber } from 'bignumber.js';11import { BigNumber } from 'bignumber.js';
63 const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());63 const bobBalanceBefore = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
6464
65 const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0);65 const badTx = api.tx.balances.setBalance(alicesPublicKey, 0, 0);
66 const result = getGenericResult(await submitTransactionAsync(bobPrivateKey, badTx));66 await expect(submitTransactionExpectFailAsync(bobPrivateKey, badTx)).to.be.rejected;
6767
68 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());68 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
69 const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());69 const bobBalanceAfter = new BigNumber((await api.query.system.account(bobsPublicKey)).data.free.toString());
70 const fee = bobBalanceBefore.minus(bobBalanceAfter);70 const fee = bobBalanceBefore.minus(bobBalanceAfter);
71 const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);71 const treasuryIncrease = treasuryBalanceAfter.minus(treasuryBalanceBefore);
7272
73 expect(result.success).to.be.false;
74 expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());73 expect(treasuryIncrease.toFixed()).to.be.equal(fee.toFixed());
75 });74 });
76 });75 });
80 const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());79 const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
81 const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());80 const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
8281
83 await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');82 await createCollectionExpectSuccess();
8483
85 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());84 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
86 const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());85 const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
96 const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());95 const treasuryBalanceBefore = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
97 const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());96 const aliceBalanceBefore = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
9897
99 await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');98 await createCollectionExpectSuccess();
10099
101 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());100 const treasuryBalanceAfter = new BigNumber((await api.query.system.account(Treasury)).data.free.toString());
102 const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());101 const aliceBalanceAfter = new BigNumber((await api.query.system.account(alicesPublicKey)).data.free.toString());
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
1import chai from 'chai';1import chai from 'chai';
2import chaiAsPromised from 'chai-as-promised';2import chaiAsPromised from 'chai-as-promised';
3import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";3import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
4import { createCollectionExpectSuccess, createCollectionExpectFailure } from "./util/helpers";4import { createCollectionExpectSuccess, createCollectionExpectFailure, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers";
5import type { AccountId, EventRecord } from '@polkadot/types/interfaces';5import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
6import privateKey from './substrate/privateKey';6import privateKey from './substrate/privateKey';
7import { nullPublicKey } from './accounts';
87
9chai.use(chaiAsPromised);8chai.use(chaiAsPromised);
10const expect = chai.expect;9const expect = chai.expect;
11
12function getDestroyResult(events: EventRecord[]): boolean {
13 let success: boolean = false;
14 events.forEach(({ phase, event: { data, method, section } }) => {
15 // console.log(` ${phase}: ${section}.${method}:: ${data}`);
16 if (method == 'ExtrinsicSuccess') {
17 success = true;
18 }
19 });
20 return success;
21}
22
23async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
24 await usingApi(async (api) => {
25 // Run the DestroyCollection transaction
26 const alicePrivateKey = privateKey(senderSeed);
27 const tx = api.tx.nft.destroyCollection(collectionId);
28 const events = await submitTransactionAsync(alicePrivateKey, tx);
29 const result = getDestroyResult(events);
30
31 // Get the collection
32 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
33
34 // What to expect
35 expect(result).to.be.true;
36 expect(collection).to.be.not.null;
37 expect(collection.Owner).to.be.equal(nullPublicKey);
38 });
39}
40
41async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
42 await usingApi(async (api) => {
43 // Run the DestroyCollection transaction
44 const alicePrivateKey = privateKey(senderSeed);
45 const tx = api.tx.nft.destroyCollection(collectionId);
46 const events = await submitTransactionAsync(alicePrivateKey, tx);
47 const result = getDestroyResult(events);
48
49 // What to expect
50 expect(result).to.be.false;
51 });
52}
5310
54describe('integration test: ext. destroyCollection():', () => {11describe('integration test: ext. destroyCollection():', () => {
55 it('NFT collection can be destroyed', async () => {12 it('NFT collection can be destroyed', async () => {
56 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');13 const collectionId = await createCollectionExpectSuccess();
57 await destroyCollectionExpectSuccess(collectionId);14 await destroyCollectionExpectSuccess(collectionId);
58 });15 });
59 it('Fungible collection can be destroyed', async () => {16 it('Fungible collection can be destroyed', async () => {
60 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');17 const collectionId = await createCollectionExpectSuccess({ mode: 'Fungible' });
61 await destroyCollectionExpectSuccess(collectionId);18 await destroyCollectionExpectSuccess(collectionId);
62 });19 });
63 it('ReFungible collection can be destroyed', async () => {20 it('ReFungible collection can be destroyed', async () => {
64 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');21 const collectionId = await createCollectionExpectSuccess({ mode: 'ReFungible' });
65 await destroyCollectionExpectSuccess(collectionId);22 await destroyCollectionExpectSuccess(collectionId);
66 });23 });
67});24});
75 });32 });
76 });33 });
77 it('(!negative test!) Destroy a collection that has already been destroyed', async () => {34 it('(!negative test!) Destroy a collection that has already been destroyed', async () => {
78 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');35 const collectionId = await createCollectionExpectSuccess();
79 await destroyCollectionExpectSuccess(collectionId);36 await destroyCollectionExpectSuccess(collectionId);
80 await destroyCollectionExpectFailure(collectionId);37 await destroyCollectionExpectFailure(collectionId);
81 });38 });
82 it('(!negative test!) Destroy a collection using non-owner account', async () => {39 it('(!negative test!) Destroy a collection using non-owner account', async () => {
83 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');40 const collectionId = await createCollectionExpectSuccess();
84 await destroyCollectionExpectFailure(collectionId, '//Bob');41 await destroyCollectionExpectFailure(collectionId, '//Bob');
85 await destroyCollectionExpectSuccess(collectionId, '//Alice');42 await destroyCollectionExpectSuccess(collectionId, '//Alice');
86 });43 });
addedtests/src/load_test_sc/loadtester.wasmdiffbeforeafterboth

binary blob — no preview

addedtests/src/load_test_sc/metadata.jsondiffbeforeafterboth

no changes

addedtests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth

no changes

addedtests/src/rpc.load.tsdiffbeforeafterboth

no changes

addedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth

no changes

modifiedtests/src/substrate/substrate-api.tsdiffbeforeafterboth
33 }33 }
34}34}
35
36enum TransactionStatus {
37 Success,
38 Fail,
39 NotReady
40}
41
42function getTransactionStatus(events: EventRecord[], status: ExtrinsicStatus): TransactionStatus {
43 if (status.isReady) {
44 return TransactionStatus.NotReady;
45 }
46 if (status.isBroadcast) {
47 return TransactionStatus.NotReady;
48 }
49 if (status.isInBlock || status.isFinalized) {
50 if(events.filter(e => e.event.data.method === 'ExtrinsicFailed').length > 0) {
51 return TransactionStatus.Fail;
52 }
53 if(events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {
54 return TransactionStatus.Success;
55 }
56 }
57
58 return TransactionStatus.Fail;
59}
3560
36export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {61export function submitTransactionAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
37 return new Promise(async function(resolve, reject) {62 return new Promise(async function(resolve, reject) {
38 try {63 try {
39 await transaction.signAndSend(sender, ({ events = [], status }) => {64 await transaction.signAndSend(sender, ({ events = [], status }) => {
65 const transactionStatus = getTransactionStatus(events, status);
66
40 if (status.isReady) {67 if (transactionStatus == TransactionStatus.Success) {
41 // nothing to do
42 // console.log(`Current tx status is Ready`);
43 } else if (status.isBroadcast) {
44 // nothing to do
45 // console.log(`Current tx status is Broadcast`);
46 } else if (status.isInBlock || status.isFinalized) {
47 resolve(events);68 resolve(events);
48 } else {69 } else if (transactionStatus == TransactionStatus.Fail) {
49 console.log(`Something went wrong with transaction. Status: ${status}`);70 console.log(`Something went wrong with transaction. Status: ${status}`);
50 reject("Transaction failed");71 reject(events);
51 }72 }
52 });73 });
53 } catch (e) {74 } catch (e) {
58}79}
5980
60export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {81export function submitTransactionExpectFailAsync(sender: IKeyringPair, transaction: SubmittableExtrinsic<ApiTypes>): Promise<EventRecord[]> {
82 const consoleError = console.error;
83 const consoleLog = console.log;
84 console.error = () => {};
85 console.log = () => {};
86
61 return new Promise(async function(resolve, reject) {87 return new Promise<EventRecord[]>(async function(res, rej) {
88 const resolve = (rec: EventRecord[]) => {
89 setTimeout(() => {
90 res(rec);
91 console.error = consoleError;
92 console.log = consoleLog;
93
94 });
95 };
96 const reject = (errror: any) => {
97 setTimeout(() => {
98 rej(errror);
99 console.error = consoleError;
100 console.log = consoleLog;
101 });
102 };
62 try {103 try {
63 await transaction.signAndSend(sender, ({ events = [], status }) => {104 await transaction.signAndSend(sender, ({ events = [], status }) => {
105 const transactionStatus = getTransactionStatus(events, status);
106
64 if (status.isReady) {107 if (transactionStatus == TransactionStatus.Success) {
65 // nothing to do
66 // console.log(`Current tx status is Ready`);
67 } else if (status.isBroadcast) {
68 // nothing to do
69 // console.log(`Current tx status is Broadcast`);
70 } else if (status.isInBlock || status.isFinalized) {
71 resolve(events);108 resolve(events);
72 } else {109 } else if (transactionStatus == TransactionStatus.Fail) {
73 reject("Transaction failed");110 reject(events);
74 }111 }
75 });112 });
76 } catch (e) {113 } catch (e) {
77 reject(e);114 reject(e);
78 }115 }
79 });116 });
80}117}
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
33 // Find unused address33 // Find unused address
34 const pk = await findUnusedAddress(api);34 const pk = await findUnusedAddress(api);
3535
36 const error = console.error;
37 const log = console.log;
36 console.log = function () {};38 console.log = function () {};
37 console.error = function () {};39 console.error = function () {};
38 40
42 };44 };
43 await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");45 await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");
4446
45 delete console.log;47 console.log = log;
46 delete console.error;48 console.error = error;
47 });49 });
48 });50 });
49});51});
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
7import chaiAsPromised from 'chai-as-promised';7import chaiAsPromised from 'chai-as-promised';
8import type { AccountId, EventRecord } from '@polkadot/types/interfaces';8import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
9import { ApiPromise, Keyring } from "@polkadot/api";9import { ApiPromise, Keyring } from "@polkadot/api";
10import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api";10import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from "../substrate/substrate-api";
11import privateKey from '../substrate/privateKey';11import privateKey from '../substrate/privateKey';
12import { alicesPublicKey } from "../accounts";12import { alicesPublicKey, nullPublicKey } from "../accounts";
13import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';13import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';
14import { IKeyringPair } from "@polkadot/types/types";14import { IKeyringPair } from "@polkadot/types/types";
15import { BigNumber } from 'bignumber.js';15import { BigNumber } from 'bignumber.js';
16import { Struct, Enum } from '@polkadot/types/codec';
17import { u128 } from '@polkadot/types/primitive';
1618
17chai.use(chaiAsPromised);19chai.use(chaiAsPromised);
18const expect = chai.expect;20const expect = chai.expect;
26 collectionId: number28 collectionId: number
27};29};
30
31type CreateItemResult = {
32 success: boolean,
33 collectionId: number,
34 itemId: number
35};
2836
29export function getGenericResult(events: EventRecord[]): GenericResult {37export function getGenericResult(events: EventRecord[]): GenericResult {
30 let result: GenericResult = {38 let result: GenericResult = {
57 return result;65 return result;
58}66}
67
68function getCreateItemResult(events: EventRecord[]): CreateItemResult {
69 let success = false;
70 let collectionId: number = 0;
71 let itemId: number = 0;
72 events.forEach(({ phase, event: { data, method, section } }) => {
73 // console.log(` ${phase}: ${section}.${method}:: ${data}`);
74 if (method == 'ExtrinsicSuccess') {
75 success = true;
76 } else if ((section == 'nft') && (method == 'ItemCreated')) {
77 collectionId = parseInt(data[0].toString());
78 itemId = parseInt(data[1].toString());
79 }
80 });
81 let result: CreateItemResult = {
82 success,
83 collectionId,
84 itemId
85 }
86 return result;
87}
88
89export type CollectionMode = 'NFT' | 'Fungible' | 'ReFungible';
90export type CreateCollectionParams = {
91 mode: CollectionMode,
92 name: string,
93 description: string,
94 tokenPrefix: string
95};
96
97const defaultCreateCollectionParams: CreateCollectionParams = {
98 name: 'name',
99 description: 'description',
100 mode: 'NFT',
101 tokenPrefix: 'prefix'
102}
59103
60export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {104export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {
105 const {name, description, mode, tokenPrefix } = {...defaultCreateCollectionParams, ...params};
106
61 let collectionId: number = 0;107 let collectionId: number = 0;
62 await usingApi(async (api) => {108 await usingApi(async (api) => {
91 return collectionId;137 return collectionId;
92}138}
93 139
94export async function createCollectionExpectFailure(name: string, description: string, tokenPrefix: string, mode: string) {140export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {
141 const {name, description, mode, tokenPrefix } = {...defaultCreateCollectionParams, ...params};
142
95 await usingApi(async (api) => {143 await usingApi(async (api) => {
96 // Get number of collections before the transaction144 // Get number of collections before the transaction
97 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());145 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());
98146
99 // Run the CreateCollection transaction147 // Run the CreateCollection transaction
100 const alicePrivateKey = privateKey('//Alice');148 const alicePrivateKey = privateKey('//Alice');
101 const tx = api.tx.nft.createCollection(name, description, tokenPrefix, mode);149 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), mode);
102 const events = await submitTransactionAsync(alicePrivateKey, tx);150 const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
103 const result = getCreateCollectionResult(events);151 const result = getCreateCollectionResult(events);
104152
105 // Get number of collections after the transaction153 // Get number of collections after the transaction
109 expect(result.success).to.be.false;157 expect(result.success).to.be.false;
110 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');158 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');
111 });159 });
112}160}
113 161
114export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {162export async function findUnusedAddress(api: ApiPromise): Promise<IKeyringPair> {
115 let bal = new BigNumber(0);163 let bal = new BigNumber(0);
123 return unused; 171 return unused;
124}172}
173
174function getDestroyResult(events: EventRecord[]): boolean {
175 let success: boolean = false;
176 events.forEach(({ phase, event: { data, method, section } }) => {
177 // console.log(` ${phase}: ${section}.${method}:: ${data}`);
178 if (method == 'ExtrinsicSuccess') {
179 success = true;
180 }
181 });
182 return success;
183}
184
185export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
186 await usingApi(async (api) => {
187 // Run the DestroyCollection transaction
188 const alicePrivateKey = privateKey(senderSeed);
189 const tx = api.tx.nft.destroyCollection(collectionId);
190 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
191 });
192}
193
194export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
195 await usingApi(async (api) => {
196 // Run the DestroyCollection transaction
197 const alicePrivateKey = privateKey(senderSeed);
198 const tx = api.tx.nft.destroyCollection(collectionId);
199 const events = await submitTransactionAsync(alicePrivateKey, tx);
200 const result = getDestroyResult(events);
201
202 // Get the collection
203 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
204
205 // What to expect
206 expect(result).to.be.true;
207 expect(collection).to.be.not.null;
208 expect(collection.Owner).to.be.equal(nullPublicKey);
209 });
210}
211
212export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {
213 await usingApi(async (api) => {
214
215 // Run the transaction
216 const alicePrivateKey = privateKey('//Alice');
217 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);
218 const events = await submitTransactionAsync(alicePrivateKey, tx);
219 const result = getGenericResult(events);
220
221 // Get the collection
222 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
223
224 // What to expect
225 expect(result.success).to.be.true;
226 expect(collection.Sponsor.toString()).to.be.equal(sponsor.toString());
227 expect(collection.SponsorConfirmed).to.be.false;
228 });
229}
230
231export async function removeCollectionSponsorExpectSuccess(collectionId: number) {
232 await usingApi(async (api) => {
233
234 // Run the transaction
235 const alicePrivateKey = privateKey('//Alice');
236 const tx = api.tx.nft.removeCollectionSponsor(collectionId);
237 const events = await submitTransactionAsync(alicePrivateKey, tx);
238 const result = getGenericResult(events);
239
240 // Get the collection
241 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
242
243 // What to expect
244 expect(result.success).to.be.true;
245 expect(collection.Sponsor).to.be.equal(nullPublicKey);
246 expect(collection.SponsorConfirmed).to.be.false;
247 });
248}
249
250export async function removeCollectionSponsorExpectFailure(collectionId: number) {
251 await usingApi(async (api) => {
252
253 // Run the transaction
254 const alicePrivateKey = privateKey('//Alice');
255 const tx = api.tx.nft.removeCollectionSponsor(collectionId);
256 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
257 });
258}
259
260export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {
261 await usingApi(async (api) => {
262
263 // Run the transaction
264 const alicePrivateKey = privateKey(senderSeed);
265 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);
266 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;
267 });
268}
269
270export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
271 await usingApi(async (api) => {
272
273 // Run the transaction
274 const sender = privateKey(senderSeed);
275 const tx = api.tx.nft.confirmSponsorship(collectionId);
276 const events = await submitTransactionAsync(sender, tx);
277 const result = getGenericResult(events);
278
279 // Get the collection
280 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
281
282 // What to expect
283 expect(result.success).to.be.true;
284 expect(collection.Sponsor).to.be.equal(sender.address);
285 expect(collection.SponsorConfirmed).to.be.true;
286 });
287}
288
289export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
290 await usingApi(async (api) => {
291
292 // Run the transaction
293 const sender = privateKey(senderSeed);
294 const tx = api.tx.nft.confirmSponsorship(collectionId);
295 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
296 });
297}
298
299export interface CreateFungibleData extends Struct {
300 readonly value: u128;
301};
302
303export interface CreateReFungibleData extends Struct {};
304export interface CreateNftData extends Struct {};
305
306export interface CreateItemData extends Enum {
307 NFT: CreateNftData,
308 Fungible: CreateFungibleData,
309 ReFungible: CreateReFungibleData
310};
125311
126export async function createItemExpectSuccess(collectionId: number, createMode: string, senderSeed: string = '//Alice') {312export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {
313 let newItemId: number = 0;
127 await usingApi(async (api) => {314 await usingApi(async (api) => {
128
129 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());315 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
130
131 const sender = privateKey(senderSeed);316 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
132 const tx = api.tx.nft.createItem(collectionId, sender.address, createMode);317 const AItemBalance = new BigNumber(Aitem.Value);
318
319 if (owner === '') owner = sender.address;
320
321 let tx;
322 if (createMode == 'Fungible') {
323 let createData = {fungible: {value: 10}};
324 tx = api.tx.nft.createItem(collectionId, owner, createData);
325 }
326 else {
327 tx = api.tx.nft.createItem(collectionId, owner, createMode);
328 }
133 const events = await submitTransactionAsync(sender, tx);329 const events = await submitTransactionAsync(sender, tx);
134 const result = getGenericResult(events);330 const result = getCreateItemResult(events);
135 331
136 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());332 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
333 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();
334 const BItemBalance = new BigNumber(Bitem.Value);
137335
138 // What to expect336 // What to expect
139 expect(result.success).to.be.true;337 expect(result.success).to.be.true;
338 if (createMode == 'Fungible') {
339 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);
340 }
341 else {
140 expect(BItemCount).to.be.equal(AItemCount+1);342 expect(BItemCount).to.be.equal(AItemCount+1);
343 }
344 expect(collectionId).to.be.equal(result.collectionId);
345 expect(BItemCount).to.be.equal(result.itemId);
346 newItemId = result.itemId;
141 });347 });
348 return newItemId;
142}349}
350
351export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
352 await usingApi(async (api) => {
353
354 // Run the transaction
355 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
356 const events = await submitTransactionAsync(sender, tx);
357 const result = getGenericResult(events);
358
359 // Get the collection
360 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
361
362 // What to expect
363 expect(result.success).to.be.true;
364 expect(collection.Access).to.be.equal('WhiteList');
365 });
366}
367
368export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {
369 await usingApi(async (api) => {
370
371 // Run the transaction
372 const tx = api.tx.nft.setMintPermission(collectionId, true);
373 const events = await submitTransactionAsync(sender, tx);
374 const result = getGenericResult(events);
375
376 // Get the collection
377 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
378
379 // What to expect
380 expect(result.success).to.be.true;
381 expect(collection.MintMode).to.be.equal(true);
382 });
383}
384
385export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
386 await usingApi(async (api) => {
387
388 // Run the transaction
389 const tx = api.tx.nft.addToWhiteList(collectionId, address);
390 const events = await submitTransactionAsync(sender, tx);
391 const result = getGenericResult(events);
392
393 // Get the collection
394 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
395
396 // What to expect
397 expect(result.success).to.be.true;
398 expect(collection.MintMode).to.be.equal(true);
399 });
400}
143401
402