difftreelog
Refactor fungible logic, add positive tests for sponsorship confirmation
in: master
4 files changed
pallets/nft/src/lib.rsdiffbeforeafterboth152152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct FungibleItemType<AccountId> {155pub struct FungibleItemType {156 pub owner: AccountId,157 pub value: u128,156 pub value: u128,158}157}159158165 pub variable_data: Vec<u8>,164 pub variable_data: Vec<u8>,166}165}167166168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct ApprovePermissions<AccountId> {171 pub approved: AccountId,172 pub amount: u128,173}174175// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177// pub struct VestingItem<AccountId, Moment> {169// pub struct VestingItem<AccountId, Moment> {183// pub vesting_date: Moment,175// pub vesting_date: Moment,184// }176// }185177186#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]187#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]188pub struct BasketItem<AccountId, BlockNumber> {189 pub address: AccountId,190 pub start_block: BlockNumber,191}192193#[derive(Encode, Decode, Debug, Clone, PartialEq)]178#[derive(Encode, Decode, Debug, Clone, PartialEq)]194#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]195pub struct CollectionLimits {180pub struct CollectionLimits {260#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]261#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]262pub struct CreateFungibleData {247pub struct CreateFungibleData {248 pub value: u128,263}249}264250265#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]414 /// Balance owner per collection map400 /// Balance owner per collection map415 pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;401 pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;416402417 /// second parameter: item id + owner account id403 /// second parameter: item id + owner account id + spender account id418 pub ApprovedList get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;404 pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;419405420 /// Item collections406 /// Item collections421 pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;407 pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;422 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => FungibleItemType<T::AccountId>;408 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;423 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;409 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;424410425 /// Index list411 /// Index list426 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;412 pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;427413428 /// Tokens transfer baskets414 /// Tokens transfer baskets429 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;415 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;430 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Vec<BasketItem<T::AccountId, T::BlockNumber>>;416 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;431 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;417 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;432418433 // Contract Sponsorship and Ownership419 // Contract Sponsorship and Ownership447 <Module<T>>::init_nft_token(*_c, _i);433 <Module<T>>::init_nft_token(*_c, _i);448 }434 }449435450 for (_num, _c, _i) in &config.fungible_item_id {436 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {451 <Module<T>>::init_fungible_token(*_c, _i);437 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);452 }438 }453439454 for (_num, _c, _i) in &config.refungible_item_id {440 for (_num, _c, _i) in &config.refungible_item_id {619 Self::check_owner_permissions(collection_id, sender)?;605 Self::check_owner_permissions(collection_id, sender)?;620606621 <AddressTokens<T>>::remove_prefix(collection_id);607 <AddressTokens<T>>::remove_prefix(collection_id);622 <ApprovedList<T>>::remove_prefix(collection_id);608 <Allowances<T>>::remove_prefix(collection_id);623 <Balance<T>>::remove_prefix(collection_id);609 <Balance<T>>::remove_prefix(collection_id);624 <ItemListIndex>::remove(collection_id);610 <ItemListIndex>::remove(collection_id);625 <AdminList<T>>::remove(collection_id);611 <AdminList<T>>::remove(collection_id);1015 /// 1001 /// 1016 /// * item_id: ID of NFT to burn.1002 /// * item_id: ID of NFT to burn.1017 #[weight = T::WeightInfo::burn_item()]1003 #[weight = T::WeightInfo::burn_item()]1018 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1004 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {101910051020 let sender = ensure_signed(origin)?;1006 let sender = ensure_signed(origin)?;1021 Self::collection_exists(collection_id)?;1007 Self::collection_exists(collection_id)?;1033 match target_collection.mode1019 match target_collection.mode1034 {1020 {1035 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1021 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1036 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,1022 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,1037 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1023 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1038 _ => ()1024 _ => ()1039 };1025 };1089 match target_collection.mode1075 match target_collection.mode1090 {1076 {1091 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1077 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1092 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1078 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1093 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1079 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1094 _ => ()1080 _ => ()1095 };1081 };1113 /// 1099 /// 1114 /// * item_id: ID of the item.1100 /// * item_id: ID of the item.1115 #[weight = T::WeightInfo::approve()]1101 #[weight = T::WeightInfo::approve()]1116 pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1102 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {111711031118 let sender = ensure_signed(origin)?;1104 let sender = ensure_signed(origin)?;11191105112511111126 if target_collection.access == AccessMode::WhiteList {1112 if target_collection.access == AccessMode::WhiteList {1127 Self::check_white_list(collection_id, &sender)?;1113 Self::check_white_list(collection_id, &sender)?;1128 Self::check_white_list(collection_id, &approved)?;1114 Self::check_white_list(collection_id, &spender)?;1129 }1115 }113011161131 // amount param stub1117 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1132 let amount = 100000000;11331134 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1135 if list_exists {1118 let mut allowance: u128 = amount;11361137 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1138 let item_contains = list.iter().any(|i| i.approved == approved);11391140 if !item_contains {1119 if allowance_exists {1141 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1120 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1142 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1143 }1144 } else {11451146 let mut list = Vec::new();1147 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1148 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1149 }1121 }1122 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);115011231151 Ok(())1124 Ok(())1152 }1125 }1176 let sender = ensure_signed(origin)?;1149 let sender = ensure_signed(origin)?;1177 let mut appoved_transfer = false;1150 let mut appoved_transfer = false;117811511179 // Check approve1152 // Check approval1180 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1153 let mut approval: u128 = 0;1154 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1181 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1155 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1182 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1183 if opt_item.is_some()1156 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1184 {1185 appoved_transfer = true;1186 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1187 }1157 appoved_transfer = true;1188 }1158 }118911591190 let target_collection = <Collection<T>>::get(collection_id);1160 let target_collection = <Collection<T>>::get(collection_id);119411641195 // Transfer permissions check 1165 // Transfer permissions check 1196 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1166 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1197 Error::<T>::NoPermission);1167 Error::<T>::NoPermission);119811681199 if target_collection.access == AccessMode::WhiteList {1169 if target_collection.access == AccessMode::WhiteList {1200 Self::check_white_list(collection_id, &sender)?;1170 Self::check_white_list(collection_id, &sender)?;1201 Self::check_white_list(collection_id, &recipient)?;1171 Self::check_white_list(collection_id, &recipient)?;1202 }1172 }120311731204 // remove approve1174 // Reduce approval by transferred amount or remove if remaining approval drops to 01205 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1175 if approval - value > 0 {1176 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1206 .into_iter().filter(|i| i.approved != sender.clone()).collect();1177 }1178 else {1207 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);1179 <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1180 }1208118112091210 match target_collection.mode1182 match target_collection.mode1211 {1183 {1212 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1184 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1213 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1185 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1214 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1186 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1215 _ => ()1187 _ => ()1216 };1188 };157415461575 Self::add_nft_item(collection_id, item)?;1547 Self::add_nft_item(collection_id, item)?;1576 },1548 },1577 CreateItemData::Fungible(_) => {1549 CreateItemData::Fungible(data) => {1578 let item = FungibleItemType {1550 Self::add_fungible_item(collection_id, &owner, data.value)?;1579 owner,1580 value: (10 as u128).pow(collection.decimal_points as u32)1581 };15821583 Self::add_fungible_item(collection_id, item)?;1584 },1551 },1585 CreateItemData::ReFungible(data) => {1552 CreateItemData::ReFungible(data) => {1586 let mut owner_list = Vec::new();1553 let mut owner_list = Vec::new();1603 Ok(())1570 Ok(())1604 }1571 }160515721606 fn add_fungible_item(collection_id: CollectionId, item: FungibleItemType<T::AccountId>) -> DispatchResult {1573 fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {1607 let current_index = <ItemListIndex>::get(collection_id)1608 .checked_add(1)1609 .ok_or(Error::<T>::NumOverflow)?;1610 let itemcopy = item.clone();1611 let owner = item.owner.clone();161215741613 Self::add_token_index(collection_id, current_index, owner.clone())?;1575 // Does new owner already have an account?1576 let mut balance: u128 = 0;1577 if <FungibleItemList<T>>::contains_key(collection_id, owner) {1578 balance = <FungibleItemList<T>>::get(collection_id, owner).value;1579 } 161415801615 <ItemListIndex>::insert(collection_id, current_index);1581 // Mint 1582 let item = FungibleItemType {1583 value: balance + value1584 };1616 <FungibleItemList<T>>::insert(collection_id, current_index, itemcopy);1585 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);161715861618 // Add current block1619 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1620 <FungibleTransferBasket<T>>::insert(collection_id, current_index, v);1621 1622 // Update balance1587 // Update balance1623 let new_balance = <Balance<T>>::get(collection_id, owner.clone())1588 let new_balance = <Balance<T>>::get(collection_id, owner)1624 .checked_add(item.value)1589 .checked_add(value)1625 .ok_or(Error::<T>::NumOverflow)?;1590 .ok_or(Error::<T>::NumOverflow)?;1626 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);1591 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);162715921628 Ok(())1593 Ok(())1629 }1594 }1642 <ItemListIndex>::insert(collection_id, current_index);1607 <ItemListIndex>::insert(collection_id, current_index);1643 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);1608 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);164416091645 // Add current block1646 let block_number: T::BlockNumber = 0.into();1647 <ReFungibleTransferBasket<T>>::insert(collection_id, current_index, block_number);16481649 // Update balance1610 // Update balance1650 let new_balance = <Balance<T>>::get(collection_id, owner.clone())1611 let new_balance = <Balance<T>>::get(collection_id, owner.clone())1651 .checked_add(value)1612 .checked_add(value)1666 <ItemListIndex>::insert(collection_id, current_index);1627 <ItemListIndex>::insert(collection_id, current_index);1667 <NftItemList<T>>::insert(collection_id, current_index, item);1628 <NftItemList<T>>::insert(collection_id, current_index, item);166816291669 // Add current block1670 let block_number: T::BlockNumber = 0.into();1671 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);16721673 // Update balance1630 // Update balance1674 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1631 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1675 .checked_add(1)1632 .checked_add(1)1697 .unwrap();1654 .unwrap();1698 Self::remove_token_index(collection_id, item_id, owner.clone())?;1655 Self::remove_token_index(collection_id, item_id, owner.clone())?;169916561700 // remove approve list1701 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17021703 // update balance1657 // update balance1704 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1658 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1705 .checked_sub(item.fraction)1659 .checked_sub(item.fraction)1719 let item = <NftItemList<T>>::get(collection_id, item_id);1673 let item = <NftItemList<T>>::get(collection_id, item_id);1720 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1674 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;172116751722 // remove approve list1723 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17241725 // update balance1676 // update balance1726 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1677 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1727 .checked_sub(1)1678 .checked_sub(1)1732 Ok(())1683 Ok(())1733 }1684 }173416851735 fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1686 fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1736 ensure!(1687 ensure!(1737 <FungibleItemList<T>>::contains_key(collection_id, item_id),1688 <FungibleItemList<T>>::contains_key(collection_id, owner),1738 Error::<T>::TokenNotFound1689 Error::<T>::TokenNotFound1739 );1690 );1740 let item = <FungibleItemList<T>>::get(collection_id, item_id);1691 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1741 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1692 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);174216931743 // remove approve list1744 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17451746 // update balance1694 // update balance1747 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1695 let new_balance = <Balance<T>>::get(collection_id, owner)1748 .checked_sub(item.value)1696 .checked_sub(value)1749 .ok_or(Error::<T>::NumOverflow)?;1697 .ok_or(Error::<T>::NumOverflow)?;1750 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1698 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);175116991752 <FungibleItemList<T>>::remove(collection_id, item_id);1700 if balance.value - value > 0 {1701 balance.value -= value;1702 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1703 }1704 else {1705 <FungibleItemList<T>>::remove(collection_id, owner);1706 }175317071754 Ok(())1708 Ok(())1755 }1709 }1810 <NftItemList<T>>::get(collection_id, item_id).owner == subject1764 <NftItemList<T>>::get(collection_id, item_id).owner == subject1811 }1765 }1812 CollectionMode::Fungible(_) => {1766 CollectionMode::Fungible(_) => {1813 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1767 <FungibleItemList<T>>::contains_key(collection_id, &subject)1814 }1768 }1815 CollectionMode::ReFungible(_) => {1769 CollectionMode::ReFungible(_) => {1816 <ReFungibleItemList<T>>::get(collection_id, item_id)1770 <ReFungibleItemList<T>>::get(collection_id, item_id)183317871834 fn transfer_fungible(1788 fn transfer_fungible(1835 collection_id: CollectionId,1789 collection_id: CollectionId,1836 item_id: TokenId,1837 value: u128,1790 value: u128,1838 owner: T::AccountId,1791 owner: &T::AccountId,1839 new_owner: T::AccountId,1792 recipient: &T::AccountId,1840 ) -> DispatchResult {1793 ) -> DispatchResult {1841 ensure!(1794 ensure!(1842 <FungibleItemList<T>>::contains_key(collection_id, item_id),1795 <FungibleItemList<T>>::contains_key(collection_id, owner),1843 Error::<T>::TokenNotFound1796 Error::<T>::TokenNotFound1844 );1797 );184517981846 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1799 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1847 let amount = full_item.value;1800 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);184818011849 ensure!(amount >= value, Error::<T>::TokenValueTooLow);1802 // Send balance to recipient (updates balanceOf of recipient)1803 Self::add_fungible_item(collection_id, recipient, value)?;185018041851 // update balance1805 // update balanceOf of sender1852 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1806 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);1853 .checked_sub(value)1854 .ok_or(Error::<T>::NumOverflow)?;1855 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);185618071857 let mut new_owner_account_id = 0;1808 // Reduce or remove sender1858 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1809 if balance.value == value {1859 if new_owner_items.len() > 0 {1860 new_owner_account_id = new_owner_items[0];1810 <FungibleItemList<T>>::remove(collection_id, owner);1861 }1811 }18621812 else {1863 // transfer1813 balance.value -= value;1864 if amount == value && new_owner_account_id == 0 {1814 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1865 // change owner1866 // new owner do not have account1867 let mut new_full_item = full_item.clone();1868 new_full_item.owner = new_owner.clone();1869 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);18701871 // update balance1872 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1873 .checked_add(value)1874 .ok_or(Error::<T>::NumOverflow)?;1875 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);18761877 // update index collection1878 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1879 } else {1880 let mut new_full_item = full_item.clone();1881 new_full_item.value -= value;18821883 // separate amount1884 if new_owner_account_id > 0 {1885 // new owner has account1886 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1887 item.value += value;18881889 // update balance1890 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())1891 .checked_add(value)1892 .ok_or(Error::<T>::NumOverflow)?;1893 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);18941895 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1896 } else {1897 // new owner do not have account1898 let item = FungibleItemType {1899 owner: new_owner.clone(),1900 value1901 };19021903 Self::add_fungible_item(collection_id, item)?;1904 }19051906 if amount == value {1907 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19081909 // remove approve list1910 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1911 <FungibleItemList<T>>::remove(collection_id, item_id);1912 }19131914 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1915 }1815 }191618161917 Ok(())1817 Ok(())2039 // update index collection1939 // update index collection2040 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1940 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;204119412042 // reset approved list2043 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2044 Ok(())1942 Ok(())2045 }1943 }2046 1944 2052 match mode {1950 match mode {2053 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1951 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2054 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1952 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2055 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2056 _ => ()1953 _ => ()2057 };1954 };2058 1955 2131 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2028 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2132 }2029 }213320302134 fn init_fungible_token(collection_id: CollectionId, item: &FungibleItemType<T::AccountId>) {2031 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2135 let current_index = <ItemListIndex>::get(collection_id)2032 let current_index = <ItemListIndex>::get(collection_id)2136 .checked_add(1)2033 .checked_add(1)2137 .unwrap();2034 .unwrap();2138 let owner = item.owner.clone();213920352140 Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();2036 Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();214120372142 <ItemListIndex>::insert(collection_id, current_index);2038 <ItemListIndex>::insert(collection_id, current_index);214320392144 // Update balance2040 // Update balance2145 let new_balance = <Balance<T>>::get(collection_id, owner.clone())2041 let new_balance = <Balance<T>>::get(collection_id, owner)2146 .checked_add(item.value)2042 .checked_add(item.value)2147 .unwrap();2043 .unwrap();2148 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2044 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2149 }2045 }215020462151 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2047 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2343 T::AccountId::default()2239 T::AccountId::default()2344 }2240 }2345 }2241 }2346 Some(Call::transfer(new_owner, collection_id, item_id, _value)) => {2242 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2347 2243 2348 let mut sponsor_transfer = false;2244 let mut sponsor_transfer = false;2349 if <Collection<T>>::get(collection_id).sponsor_confirmed {2245 if <Collection<T>>::get(collection_id).sponsor_confirmed {2352 let collection_mode = <Collection<T>>::get(collection_id).mode;2248 let collection_mode = <Collection<T>>::get(collection_id).mode;2353 2249 2354 // sponsor timeout2250 // sponsor timeout2251 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2355 sponsor_transfer = match collection_mode {2252 sponsor_transfer = match collection_mode {2356 CollectionMode::NFT => {2253 CollectionMode::NFT => {2357 2254 2362 ChainLimit::get().nft_sponsor_transfer_timeout2259 ChainLimit::get().nft_sponsor_transfer_timeout2363 };2260 };2364 2261 2365 let basket = <NftTransferBasket<T>>::get(collection_id, item_id);2262 let mut sponsored = true;2263 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2366 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2264 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2367 let limit_time = basket + limit.into();2265 let limit_time = last_tx_block + limit.into();2368 if block_number >= limit_time {2266 if block_number <= limit_time {2267 sponsored = false;2268 }2269 }2270 if sponsored {2369 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2271 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2370 true2371 }2272 }2372 else {22732373 false2374 }2274 sponsored2375 }2275 }2376 CollectionMode::Fungible(_) => {2276 CollectionMode::Fungible(_) => {2377 2277 2382 ChainLimit::get().fungible_sponsor_transfer_timeout2282 ChainLimit::get().fungible_sponsor_transfer_timeout2383 };2283 };2384 2284 2385 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, item_id);2386 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2285 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2387 if basket.iter().any(|i| i.address == new_owner.clone())2286 let mut sponsored = true;2388 {2287 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2389 let item = basket.iter_mut().find(|i| i.address == new_owner.clone()).unwrap().clone();2288 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2390 let limit_time = item.start_block + limit.into();2289 let limit_time = last_tx_block + limit.into();2391 if block_number >= limit_time {2290 if block_number <= limit_time {2392 basket.retain(|x| x.address == item.address);2291 sponsored = false;2393 basket.push(BasketItem { start_block: block_number, address: new_owner.clone() });2394 <FungibleTransferBasket<T>>::insert(collection_id, item_id, basket);2395 true2396 }2292 }2397 else {2398 false2399 }2400 }2293 }2401 else {2294 if sponsored {2402 basket.push(BasketItem { start_block: block_number, address: new_owner.clone()});2295 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2403 true2404 }2296 }22972298 sponsored2405 }2299 }2406 CollectionMode::ReFungible(_) => {2300 CollectionMode::ReFungible(_) => {2407 2301 2412 ChainLimit::get().refungible_sponsor_transfer_timeout2306 ChainLimit::get().refungible_sponsor_transfer_timeout2413 };2307 };2414 2308 2415 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2309 let mut sponsored = true;2310 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2416 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2311 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2417 let limit_time = basket + limit.into();2312 let limit_time = last_tx_block + limit.into();2418 if block_number >= limit_time {2313 if block_number <= limit_time {2314 sponsored = false;2315 }2316 }2317 if sponsored {2419 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2318 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2420 true2421 } else {2422 false2423 }2319 }23202321 sponsored2424 }2322 }2425 _ => {2323 _ => {2426 false2324 falseruntime_types.jsondiffbeforeafterboth42 "Fraction": "u128"42 "Fraction": "u128"43 },43 },44 "FungibleItemType": {44 "FungibleItemType": {45 "Owner": "AccountId",46 "Value": "u128"45 "Value": "u128"47 },46 },48 "NftItemType": {47 "NftItemType": {72 "VariableOnChainSchema": "Vec<u8>",71 "VariableOnChainSchema": "Vec<u8>",73 "ConstOnChainSchema": "Vec<u8>"72 "ConstOnChainSchema": "Vec<u8>"74 },73 },75 "ApprovePermissions": {76 "Approved": "AccountId",77 "Amount": "u128"78 },79 "RawData": "Vec<u8>",74 "RawData": "Vec<u8>",80 "Address": "AccountId",75 "Address": "AccountId",81 "LookupSource": "AccountId",76 "LookupSource": "AccountId",85 "variable_data": "Vec<u8>" 80 "variable_data": "Vec<u8>" 86 },81 },87 "CreateFungibleData": {},82 "CreateFungibleData": {83 "value": "u128"84 },88 "CreateReFungibleData": {85 "CreateReFungibleData": {89 "const_data": "Vec<u8>",86 "const_data": "Vec<u8>",104 },101 },105 "CollectionId": "u32",102 "CollectionId": "u32",106 "TokenId": "u32",103 "TokenId": "u32",107 "BasketItem": {108 "Address": "AccountId",109 "start_block": "BlockNumber"110 },111 "ChainLimits": {104 "ChainLimits": {112 "collection_numbers_limit": "u32",105 "collection_numbers_limit": "u32",113 "account_token_ownership_limit": "u32",106 "account_token_ownership_limit": "u32",tests/src/confirmSponsorship.test.tsdiffbeforeafterboth16 createItemExpectSuccess,16 createItemExpectSuccess,17 findUnusedAddress,17 findUnusedAddress,18 getGenericResult,18 getGenericResult,19 enableWhiteListExpectSuccess,19} from "./util/helpers";20} from "./util/helpers";20import { Keyring } from "@polkadot/api";21import { Keyring } from "@polkadot/api";21import { IKeyringPair } from "@polkadot/types/types";22import { IKeyringPair } from "@polkadot/types/types";72 // Mint token for unused address73 // Mint token for unused address73 const itemId = await createItemExpectSuccess(collectionId, 'NFT', zeroBalance.address, '//Alice');74 const itemId = await createItemExpectSuccess(collectionId, 'NFT', zeroBalance.address, '//Alice');747575 // Transfer this token from unused address to Alice76 // Transfer this tokens from unused address to Alice76 const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 0);77 const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 0);77 const events = await submitTransactionAsync(zeroBalance, zeroToAlice);78 const events = await submitTransactionAsync(zeroBalance, zeroToAlice);78 const result = getGenericResult(events);79 const result = getGenericResult(events);86 });87 });878888 it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {89 it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {90 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');91 await setCollectionSponsorExpectSuccess(collectionId, bob.address);92 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');9394 await usingApi(async (api) => {95 const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());9697 // Find unused address98 const zeroBalance = await findUnusedAddress(api);99100 // Mint token for unused address101 const itemId = await createItemExpectSuccess(collectionId, 'Fungible', zeroBalance.address, '//Alice');102103 // Transfer this tokens from unused address to Alice104 const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);105 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);106 const result1 = getGenericResult(events1);107108 const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());10989 expect(false).to.be.true;110 expect(result1.success).to.be.true;111 expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;112 });90 });113 });9111492 it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {115 it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async () => {116 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');117 await setCollectionSponsorExpectSuccess(collectionId, bob.address);118 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');119120 await usingApi(async (api) => {121 const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());122123 // Find unused address124 const zeroBalance = await findUnusedAddress(api);125126 // Mint token for unused address127 const itemId = await createItemExpectSuccess(collectionId, 'ReFungible', zeroBalance.address, '//Alice');128129 // Transfer this tokens from unused address to Alice130 const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);131 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);132 const result1 = getGenericResult(events1);133134 const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());13593 expect(false).to.be.true;136 expect(result1.success).to.be.true;137 expect(BsponsorBalance.lt(AsponsorBalance)).to.be.true;138 });94 });139 });9514096 it.skip('CreateItem fees are paid by the sponsor after confirmation', async () => {141 it.only('CreateItem fees are paid by the sponsor after confirmation', async () => {97 // const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');98 // await setCollectionSponsorExpectSuccess(collectionId, bob.address);99 // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');100 expect(false).to.be.true;142 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');143 await setCollectionSponsorExpectSuccess(collectionId, bob.address);144 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');145146 // Enable collection white list 147 await enableWhiteListExpectSuccess(collectionId);148149 // Enable public minting150151 // Create Item152153154101 });155 });102156103 it('NFT: Sponsoring is rate limited', async () => {157 it('NFT: Sponsoring is rate limited', async () => {158 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'NFT');159 await setCollectionSponsorExpectSuccess(collectionId, bob.address);160 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');161162 await usingApi(async (api) => {163 // Find unused address164 const zeroBalance = await findUnusedAddress(api);165166 // Mint token for alice167 const itemId = await createItemExpectSuccess(collectionId, 'NFT', alice.address, '//Alice');168169 // Transfer this token from Alice to unused address and back170 // Alice to Zero gets sponsored171 const aliceToZero = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 0);172 const events1 = await submitTransactionAsync(alice, aliceToZero);173 const result1 = getGenericResult(events1);174175 // Second transfer should fail176 const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());177 const zeroToAlice = api.tx.nft.transfer(alice.address, collectionId, itemId, 0);178 const badTransaction = async function () { 179 console.log = function () {};180 console.error = function () {};181 await submitTransactionAsync(zeroBalance, zeroToAlice);182 delete console.log;183 delete console.error;184 };104 expect(false).to.be.true;185 await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");186 const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());187188 // Try again after Zero gets some balance - now it should succeed189 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);190 await submitTransactionAsync(alice, balancetx);191 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);192 const result2 = getGenericResult(events2);193194 expect(result1.success).to.be.true;195 expect(result2.success).to.be.true;196 expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;197 });105 });198 });106199107 it('Fungible: Sponsoring is rate limited', async () => {200 it('Fungible: Sponsoring is rate limited', async () => {201 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'Fungible');202 await setCollectionSponsorExpectSuccess(collectionId, bob.address);203 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');204205 await usingApi(async (api) => {206 // Find unused address207 const zeroBalance = await findUnusedAddress(api);208209 // Mint token for unused address210 const itemId = await createItemExpectSuccess(collectionId, 'Fungible', zeroBalance.address, '//Alice');211212 // Transfer this tokens in parts from unused address to Alice213 const zeroToAlice = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);214 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);215 const result1 = getGenericResult(events1);216217 const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());218219 const badTransaction = async function () { 220 console.log = function () {};221 console.error = function () {};222 await submitTransactionAsync(zeroBalance, zeroToAlice);223 delete console.log;224 delete console.error;225 };226227 const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());228229 // Try again after Zero gets some balance - now it should succeed230 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);231 await submitTransactionAsync(alice, balancetx);232 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);233 const result2 = getGenericResult(events2);234108 expect(false).to.be.true;235 expect(result1.success).to.be.true;236 expect(result2.success).to.be.true;237 expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;238 });109 });239 });110240111 it('ReFungible: Sponsoring is rate limited', async () => {241 it('ReFungible: Sponsoring is rate limited', async () => {242 const collectionId = await createCollectionExpectSuccess('A', 'B', 'C', 'ReFungible');243 await setCollectionSponsorExpectSuccess(collectionId, bob.address);244 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');245246 await usingApi(async (api) => {247 // Find unused address248 const zeroBalance = await findUnusedAddress(api);249250 // Mint token for alice251 const itemId = await createItemExpectSuccess(collectionId, 'ReFungible', alice.address, '//Alice');252253 // Transfer this token from Alice to unused address and back254 // Alice to Zero gets sponsored255 const aliceToZero = api.tx.nft.transfer(zeroBalance.address, collectionId, itemId, 1);256 const events1 = await submitTransactionAsync(alice, aliceToZero);257 const result1 = getGenericResult(events1);258259 // Second transfer should fail260 const AsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());261 const zeroToAlice = api.tx.nft.transfer(alice.address, collectionId, itemId, 1);262 const badTransaction = async function () { 263 console.log = function () {};264 console.error = function () {};265 await submitTransactionAsync(zeroBalance, zeroToAlice);266 delete console.log;267 delete console.error;268 };112 expect(false).to.be.true;269 await expect(badTransaction()).to.be.rejectedWith("Inability to pay some fees");270 const BsponsorBalance = new BigNumber((await api.query.system.account(bob.address)).data.free.toString());271272 // Try again after Zero gets some balance - now it should succeed273 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);274 await submitTransactionAsync(alice, balancetx);275 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);276 const result2 = getGenericResult(events2);277278 expect(result1.success).to.be.true;279 expect(result2.success).to.be.true;280 expect(BsponsorBalance.isEqualTo(AsponsorBalance)).to.be.true;281 });113 });282 });114283115});284});tests/src/util/helpers.tsdiffbeforeafterboth13import { 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';161817chai.use(chaiAsPromised);19chai.use(chaiAsPromised);18const expect = chai.expect;20const expect = chai.expect;258 });260 });259}261}262263export interface CreateFungibleData extends Struct {264 readonly value: u128;265};266267export interface CreateReFungibleData extends Struct {};268export interface CreateNftData extends Struct {};269270export interface CreateItemData extends Enum {271 NFT: CreateNftData,272 Fungible: CreateFungibleData,273 ReFungible: CreateReFungibleData274};260275261export async function createItemExpectSuccess(collectionId: number, createMode: string, owner: string = '', senderSeed: string = '//Alice') {276export async function createItemExpectSuccess(collectionId: number, createMode: string, owner: string = '', senderSeed: string = '//Alice') {262 let newItemId: number = 0;277 let newItemId: number = 0;263 await usingApi(async (api) => {278 await usingApi(async (api) => {264 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());279 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());280 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON(); 281 const AItemBalance = new BigNumber(Aitem.Value);265282266 const sender = privateKey(senderSeed);283 const sender = privateKey(senderSeed);267 if (owner === '') owner = sender.address;284 if (owner === '') owner = sender.address;285286 let tx;287 if (createMode == 'Fungible') {288 let createData = {fungible: {value: 10}};289 tx = api.tx.nft.createItem(collectionId, owner, createData);290 }291 else {268 const tx = api.tx.nft.createItem(collectionId, owner, createMode);292 tx = api.tx.nft.createItem(collectionId, owner, createMode);293 }269 const events = await submitTransactionAsync(sender, tx);294 const events = await submitTransactionAsync(sender, tx);270 const result = getCreateItemResult(events);295 const result = getCreateItemResult(events);271 296272 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());297 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());298 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON(); 299 const BItemBalance = new BigNumber(Bitem.Value);273300274 // What to expect301 // What to expect275 expect(result.success).to.be.true;302 expect(result.success).to.be.true;303 if (createMode == 'Fungible') {304 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);305 }306 else {276 expect(BItemCount).to.be.equal(AItemCount+1);307 expect(BItemCount).to.be.equal(AItemCount+1);308 }277 expect(collectionId).to.be.equal(result.collectionId);309 expect(collectionId).to.be.equal(result.collectionId);278 expect(BItemCount).to.be.equal(result.itemId);310 expect(BItemCount).to.be.equal(result.itemId);279 newItemId = result.itemId;311 newItemId = result.itemId;280 });312 });281 return newItemId;313 return newItemId;282}314}315316export async function enableWhiteListExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {317 await usingApi(async (api) => {318319 // Run the transaction320 const sender = privateKey(senderSeed);321 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');322 const events = await submitTransactionAsync(sender, tx);323 const result = getGenericResult(events);324325 // Get the collection 326 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();327328 // What to expect329 expect(result.success).to.be.true;330 expect(collection.Access).to.be.equal('WhiteList');331 });332}283333