difftreelog
Merge pull request #50 from usetech-llc/feature/NFTPAR-250
in: master
NFTPAR-250 Tests for collection sponsoring
9 files changed
node/src/chain_spec.rsdiffbeforeafterboth193 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()pallets/nft/src/lib.rsdiffbeforeafterboth136 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 sender139 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}153152154#[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}161158162#[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}170166171#[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// }177177178#[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}188189#[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}195196#[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 {263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {247pub struct CreateFungibleData {248 pub value: u128,266}249}267250268#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]417 /// Balance owner per collection map400 /// Balance owner per collection map418 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;419402420 /// second parameter: item id + owner account id403 /// second parameter: item id + owner account id + spender account id421 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;422405423 /// Item collections406 /// Item collections424 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>;425 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;426 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>;427410428 /// Index list411 /// Index list429 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>;430413431 /// Tokens transfer baskets414 /// Tokens transfer baskets432 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;433 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;434 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;435418436 // Contract Sponsorship and Ownership419 // Contract Sponsorship and Ownership448 <Module<T>>::init_collection(_c);431 <Module<T>>::init_collection(_c);449 }432 }450433451 for (_num, _q, _i) in &config.nft_item_id {434 for (_num, _c, _i) in &config.nft_item_id {452 <Module<T>>::init_nft_token(_i);435 <Module<T>>::init_nft_token(*_c, _i);453 }436 }454437455 for (_num, _q, _i) in &config.fungible_item_id {438 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {456 <Module<T>>::init_fungible_token(_i);439 <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);457 }440 }458441459 for (_num, _q, _i) in &config.refungible_item_id {442 for (_num, _c, _i) in &config.refungible_item_id {460 <Module<T>>::init_refungible_token(_i);443 <Module<T>>::init_refungible_token(*_c, _i);461 }444 }462 })445 })463 }446 }593 offchain_schema: Vec::new(),576 offchain_schema: Vec::new(),594 schema_version: SchemaVersion::ImageURL,577 schema_version: SchemaVersion::ImageURL,595 sponsor: T::AccountId::default(),578 sponsor: T::AccountId::default(),596 unconfirmed_sponsor: T::AccountId::default(),579 sponsor_confirmed: false,597 variable_on_chain_schema: Vec::new(),580 variable_on_chain_schema: Vec::new(),598 const_on_chain_schema: Vec::new(),581 const_on_chain_schema: Vec::new(),599 limits: CollectionLimits::default(),582 limits: CollectionLimits::default(),624 Self::check_owner_permissions(collection_id, sender)?;607 Self::check_owner_permissions(collection_id, sender)?;625608626 <AddressTokens<T>>::remove_prefix(collection_id);609 <AddressTokens<T>>::remove_prefix(collection_id);627 <ApprovedList<T>>::remove_prefix(collection_id);610 <Allowances<T>>::remove_prefix(collection_id);628 <Balance<T>>::remove_prefix(collection_id);611 <Balance<T>>::remove_prefix(collection_id);629 <ItemListIndex>::remove(collection_id);612 <ItemListIndex>::remove(collection_id);630 <AdminList<T>>::remove(collection_id);613 <AdminList<T>>::remove(collection_id);852 let mut target_collection = <Collection<T>>::get(collection_id);835 let mut target_collection = <Collection<T>>::get(collection_id);853 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);836 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);854837855 target_collection.unconfirmed_sponsor = new_sponsor;838 target_collection.sponsor = new_sponsor;839 target_collection.sponsor_confirmed = false;856 <Collection<T>>::insert(collection_id, target_collection);840 <Collection<T>>::insert(collection_id, target_collection);857841858 Ok(())842 Ok(())872 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);856 ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);873857874 let mut target_collection = <Collection<T>>::get(collection_id);858 let mut target_collection = <Collection<T>>::get(collection_id);875 ensure!(sender == target_collection.unconfirmed_sponsor, Error::<T>::ConfirmUnsetSponsorFail);859 ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);876860877 target_collection.sponsor = target_collection.unconfirmed_sponsor;861 target_collection.sponsor_confirmed = true;878 target_collection.unconfirmed_sponsor = T::AccountId::default();879 <Collection<T>>::insert(collection_id, target_collection);862 <Collection<T>>::insert(collection_id, target_collection);880863881 Ok(())864 Ok(())900 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);883 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);901884902 target_collection.sponsor = T::AccountId::default();885 target_collection.sponsor = T::AccountId::default();886 target_collection.sponsor_confirmed = false;903 <Collection<T>>::insert(collection_id, target_collection);887 <Collection<T>>::insert(collection_id, target_collection);904888905 Ok(())889 Ok(())1000 /// 984 /// 1001 /// * item_id: ID of NFT to burn.985 /// * item_id: ID of NFT to burn.1002 #[weight = T::WeightInfo::burn_item()]986 #[weight = T::WeightInfo::burn_item()]1003 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {987 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10049881005 let sender = ensure_signed(origin)?;989 let sender = ensure_signed(origin)?;1006 Self::collection_exists(collection_id)?;990 Self::collection_exists(collection_id)?;1018 match target_collection.mode1002 match target_collection.mode1019 {1003 {1020 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1004 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1021 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,1005 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?,1022 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1006 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,1023 _ => ()1007 _ => ()1024 };1008 };1074 match target_collection.mode1058 match target_collection.mode1075 {1059 {1076 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1060 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,1077 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,1061 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1078 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1062 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,1079 _ => ()1063 _ => ()1080 };1064 };1098 /// 1082 /// 1099 /// * item_id: ID of the item.1083 /// * item_id: ID of the item.1100 #[weight = T::WeightInfo::approve()]1084 #[weight = T::WeightInfo::approve()]1101 pub fn approve(origin, approved: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1085 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {110210861103 let sender = ensure_signed(origin)?;1087 let sender = ensure_signed(origin)?;11041088111010941111 if target_collection.access == AccessMode::WhiteList {1095 if target_collection.access == AccessMode::WhiteList {1112 Self::check_white_list(collection_id, &sender)?;1096 Self::check_white_list(collection_id, &sender)?;1113 Self::check_white_list(collection_id, &approved)?;1097 Self::check_white_list(collection_id, &spender)?;1114 }1098 }111510991116 // amount param stub1100 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1117 let amount = 100000000;11181119 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1120 if list_exists {1101 let mut allowance: u128 = amount;11211122 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1123 let item_contains = list.iter().any(|i| i.approved == approved);11241125 if !item_contains {1102 if allowance_exists {1126 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1103 allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1127 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1128 }1129 } else {11301131 let mut list = Vec::new();1132 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1133 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1134 }1104 }1105 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);113511061136 Ok(())1107 Ok(())1137 }1108 }1161 let sender = ensure_signed(origin)?;1132 let sender = ensure_signed(origin)?;1162 let mut appoved_transfer = false;1133 let mut appoved_transfer = false;116311341164 // Check approve1135 // Check approval1165 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1136 let mut approval: u128 = 0;1137 if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &recipient)) {1166 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1138 approval = <Allowances<T>>::get(collection_id, (item_id, &from, &recipient));1167 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1168 if opt_item.is_some()1139 ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1169 {1170 appoved_transfer = true;1171 ensure!(opt_item.unwrap().amount >= value, Error::<T>::TokenValueNotEnough);1172 }1140 appoved_transfer = true;1173 }1141 }117411421175 let target_collection = <Collection<T>>::get(collection_id);1143 let target_collection = <Collection<T>>::get(collection_id);117911471180 // Transfer permissions check 1148 // Transfer permissions check 1181 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1149 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1182 Error::<T>::NoPermission);1150 Error::<T>::NoPermission);118311511184 if target_collection.access == AccessMode::WhiteList {1152 if target_collection.access == AccessMode::WhiteList {1185 Self::check_white_list(collection_id, &sender)?;1153 Self::check_white_list(collection_id, &sender)?;1186 Self::check_white_list(collection_id, &recipient)?;1154 Self::check_white_list(collection_id, &recipient)?;1187 }1155 }118811561189 // remove approve1157 // Reduce approval by transferred amount or remove if remaining approval drops to 01190 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1158 if approval - value > 0 {1159 <Allowances<T>>::insert(collection_id, (item_id, &from, &recipient), approval - value);1191 .into_iter().filter(|i| i.approved != sender.clone()).collect();1160 }1161 else {1192 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);1162 <Allowances<T>>::remove(collection_id, (item_id, &from, &recipient));1163 }1193116411941195 match target_collection.mode1165 match target_collection.mode1196 {1166 {1197 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1167 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1198 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1168 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1199 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1169 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1200 _ => ()1170 _ => ()1201 };1171 };1614 {1584 {1615 CreateItemData::NFT(data) => {1585 CreateItemData::NFT(data) => {1616 let item = NftItemType {1586 let item = NftItemType {1617 collection: collection_id,1618 owner,1587 owner,1619 const_data: data.const_data,1588 const_data: data.const_data,1620 variable_data: data.variable_data1589 variable_data: data.variable_data1621 };1590 };162215911623 Self::add_nft_item(item)?;1592 Self::add_nft_item(collection_id, item)?;1624 },1593 },1625 CreateItemData::Fungible(_) => {1594 CreateItemData::Fungible(data) => {1626 let item = FungibleItemType {1595 Self::add_fungible_item(collection_id, &owner, data.value)?;1627 collection: collection_id,1628 owner,1629 value: (10 as u128).pow(collection.decimal_points as u32)1630 };16311632 Self::add_fungible_item(item)?;1633 },1596 },1634 CreateItemData::ReFungible(data) => {1597 CreateItemData::ReFungible(data) => {1635 let mut owner_list = Vec::new();1598 let mut owner_list = Vec::new();1636 let value = (10 as u128).pow(collection.decimal_points as u32);1599 let value = (10 as u128).pow(collection.decimal_points as u32);1637 owner_list.push(Ownership {owner: owner.clone(), fraction: value});1600 owner_list.push(Ownership {owner: owner.clone(), fraction: value});163816011639 let item = ReFungibleItemType {1602 let item = ReFungibleItemType {1640 collection: collection_id,1641 owner: owner_list,1603 owner: owner_list,1642 const_data: data.const_data,1604 const_data: data.const_data,1643 variable_data: data.variable_data1605 variable_data: data.variable_data1644 };1606 };164516071646 Self::add_refungible_item(item)?;1608 Self::add_refungible_item(collection_id, item)?;1647 }1609 }1648 };1610 };164916111653 Ok(())1615 Ok(())1654 }1616 }165516171656 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1618 fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {1657 let current_index = <ItemListIndex>::get(item.collection)1658 .checked_add(1)1659 .ok_or(Error::<T>::NumOverflow)?;1660 let itemcopy = item.clone();1661 let owner = item.owner.clone();166216191663 Self::add_token_index(item.collection, current_index, owner.clone())?;1620 // Does new owner already have an account?1621 let mut balance: u128 = 0;1622 if <FungibleItemList<T>>::contains_key(collection_id, owner) {1623 balance = <FungibleItemList<T>>::get(collection_id, owner).value;1624 } 166416251665 <ItemListIndex>::insert(item.collection, current_index);1626 // Mint 1627 let item = FungibleItemType {1628 value: balance + value1629 };1666 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);1630 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);166716311668 // Add current block1669 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1670 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1671 1672 // Update balance1632 // Update balance1673 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1633 let new_balance = <Balance<T>>::get(collection_id, owner)1674 .checked_add(item.value)1634 .checked_add(value)1675 .ok_or(Error::<T>::NumOverflow)?;1635 .ok_or(Error::<T>::NumOverflow)?;1676 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1636 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);167716371678 Ok(())1638 Ok(())1679 }1639 }168016401681 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1641 fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1682 let current_index = <ItemListIndex>::get(item.collection)1642 let current_index = <ItemListIndex>::get(collection_id)1683 .checked_add(1)1643 .checked_add(1)1684 .ok_or(Error::<T>::NumOverflow)?;1644 .ok_or(Error::<T>::NumOverflow)?;1685 let itemcopy = item.clone();1645 let itemcopy = item.clone();168616461687 let value = item.owner.first().unwrap().fraction;1647 let value = item.owner.first().unwrap().fraction;1688 let owner = item.owner.first().unwrap().owner.clone();1648 let owner = item.owner.first().unwrap().owner.clone();168916491690 Self::add_token_index(item.collection, current_index, owner.clone())?;1650 Self::add_token_index(collection_id, current_index, owner.clone())?;169116511692 <ItemListIndex>::insert(item.collection, current_index);1652 <ItemListIndex>::insert(collection_id, current_index);1693 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);1653 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);169416541695 // Add current block1696 let block_number: T::BlockNumber = 0.into();1697 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);16981699 // Update balance1655 // Update balance1700 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1656 let new_balance = <Balance<T>>::get(collection_id, owner.clone())1701 .checked_add(value)1657 .checked_add(value)1702 .ok_or(Error::<T>::NumOverflow)?;1658 .ok_or(Error::<T>::NumOverflow)?;1703 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1659 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);170416601705 Ok(())1661 Ok(())1706 }1662 }170716631708 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1664 fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1709 let current_index = <ItemListIndex>::get(item.collection)1665 let current_index = <ItemListIndex>::get(collection_id)1710 .checked_add(1)1666 .checked_add(1)1711 .ok_or(Error::<T>::NumOverflow)?;1667 .ok_or(Error::<T>::NumOverflow)?;171216681713 let item_owner = item.owner.clone();1669 let item_owner = item.owner.clone();1714 let collection_id = item.collection.clone();1715 Self::add_token_index(collection_id, current_index, item.owner.clone())?;1670 Self::add_token_index(collection_id, current_index, item.owner.clone())?;171616711717 <ItemListIndex>::insert(collection_id, current_index);1672 <ItemListIndex>::insert(collection_id, current_index);1718 <NftItemList<T>>::insert(collection_id, current_index, item);1673 <NftItemList<T>>::insert(collection_id, current_index, item);171916741720 // Add current block1721 let block_number: T::BlockNumber = 0.into();1722 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);17231724 // Update balance1675 // Update balance1725 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1676 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1726 .checked_add(1)1677 .checked_add(1)1748 .unwrap();1699 .unwrap();1749 Self::remove_token_index(collection_id, item_id, owner.clone())?;1700 Self::remove_token_index(collection_id, item_id, owner.clone())?;175017011751 // remove approve list1752 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));17531754 // update balance1702 // update balance1755 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1703 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1756 .checked_sub(item.fraction)1704 .checked_sub(item.fraction)1770 let item = <NftItemList<T>>::get(collection_id, item_id);1718 let item = <NftItemList<T>>::get(collection_id, item_id);1771 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1719 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;177217201773 // remove approve list1774 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17751776 // update balance1721 // update balance1777 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1722 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1778 .checked_sub(1)1723 .checked_sub(1)1783 Ok(())1728 Ok(())1784 }1729 }178517301786 fn burn_fungible_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1731 fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1787 ensure!(1732 ensure!(1788 <FungibleItemList<T>>::contains_key(collection_id, item_id),1733 <FungibleItemList<T>>::contains_key(collection_id, owner),1789 Error::<T>::TokenNotFound1734 Error::<T>::TokenNotFound1790 );1735 );1791 let item = <FungibleItemList<T>>::get(collection_id, item_id);1736 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1792 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;1737 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);179317381794 // remove approve list1795 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));17961797 // update balance1739 // update balance1798 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1740 let new_balance = <Balance<T>>::get(collection_id, owner)1799 .checked_sub(item.value)1741 .checked_sub(value)1800 .ok_or(Error::<T>::NumOverflow)?;1742 .ok_or(Error::<T>::NumOverflow)?;1801 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1743 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);180217441803 <FungibleItemList<T>>::remove(collection_id, item_id);1745 if balance.value - value > 0 {1746 balance.value -= value;1747 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1748 }1749 else {1750 <FungibleItemList<T>>::remove(collection_id, owner);1751 }180417521805 Ok(())1753 Ok(())1806 }1754 }1861 <NftItemList<T>>::get(collection_id, item_id).owner == subject1809 <NftItemList<T>>::get(collection_id, item_id).owner == subject1862 }1810 }1863 CollectionMode::Fungible(_) => {1811 CollectionMode::Fungible(_) => {1864 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1812 <FungibleItemList<T>>::contains_key(collection_id, &subject)1865 }1813 }1866 CollectionMode::ReFungible(_) => {1814 CollectionMode::ReFungible(_) => {1867 <ReFungibleItemList<T>>::get(collection_id, item_id)1815 <ReFungibleItemList<T>>::get(collection_id, item_id)188218301883 fn transfer_fungible(1831 fn transfer_fungible(1884 collection_id: CollectionId,1832 collection_id: CollectionId,1885 item_id: TokenId,1886 value: u128,1833 value: u128,1887 owner: T::AccountId,1834 owner: &T::AccountId,1888 new_owner: T::AccountId,1835 recipient: &T::AccountId,1889 ) -> DispatchResult {1836 ) -> DispatchResult {1890 ensure!(1837 ensure!(1891 <FungibleItemList<T>>::contains_key(collection_id, item_id),1838 <FungibleItemList<T>>::contains_key(collection_id, owner),1892 Error::<T>::TokenNotFound1839 Error::<T>::TokenNotFound1893 );1840 );189418411895 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1842 let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1896 let amount = full_item.value;1843 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);189718441898 ensure!(amount >= value, Error::<T>::TokenValueTooLow);1845 // Send balance to recipient (updates balanceOf of recipient)1846 Self::add_fungible_item(collection_id, recipient, value)?;189918471900 // update balance1848 // update balanceOf of sender1901 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1849 <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);1902 .checked_sub(value)1903 .ok_or(Error::<T>::NumOverflow)?;1904 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);190518501906 let mut new_owner_account_id = 0;1851 // Reduce or remove sender1907 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1852 if balance.value == value {1908 if new_owner_items.len() > 0 {1909 new_owner_account_id = new_owner_items[0];1853 <FungibleItemList<T>>::remove(collection_id, owner);1910 }1854 }19111855 else {1912 // transfer1856 balance.value -= value;1913 if amount == value && new_owner_account_id == 0 {1857 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1914 // change owner1915 // new owner do not have account1916 let mut new_full_item = full_item.clone();1917 new_full_item.owner = new_owner.clone();1918 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);19191920 // update balance1921 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1922 .checked_add(value)1923 .ok_or(Error::<T>::NumOverflow)?;1924 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19251926 // update index collection1927 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1928 } else {1929 let mut new_full_item = full_item.clone();1930 new_full_item.value -= value;19311932 // separate amount1933 if new_owner_account_id > 0 {1934 // new owner has account1935 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1936 item.value += value;19371938 // update balance1939 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1940 .checked_add(value)1941 .ok_or(Error::<T>::NumOverflow)?;1942 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);19431944 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1945 } else {1946 // new owner do not have account1947 let item = FungibleItemType {1948 collection: collection_id,1949 owner: new_owner.clone(),1950 value1951 };19521953 Self::add_fungible_item(item)?;1954 }19551956 if amount == value {1957 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;19581959 // remove approve list1960 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1961 <FungibleItemList<T>>::remove(collection_id, item_id);1962 }19631964 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1965 }1858 }196618591967 Ok(())1860 Ok(())1996 .ok_or(Error::<T>::NumOverflow)?;1889 .ok_or(Error::<T>::NumOverflow)?;1997 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);1890 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);199818911999 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1892 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2000 .checked_add(value)1893 .checked_add(value)2001 .ok_or(Error::<T>::NumOverflow)?;1894 .ok_or(Error::<T>::NumOverflow)?;2002 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);1895 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);200318962004 let old_owner = item.owner.clone();1897 let old_owner = item.owner.clone();2005 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1898 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);2076 .ok_or(Error::<T>::NumOverflow)?;1969 .ok_or(Error::<T>::NumOverflow)?;2077 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);1970 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);207819712079 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1972 let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2080 .checked_add(1)1973 .checked_add(1)2081 .ok_or(Error::<T>::NumOverflow)?;1974 .ok_or(Error::<T>::NumOverflow)?;2082 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);1975 <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);208319762084 // change owner1977 // change owner2085 let old_owner = item.owner.clone();1978 let old_owner = item.owner.clone();2089 // update index collection1982 // update index collection2090 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1983 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;209119842092 // reset approved list2093 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));2094 Ok(())1985 Ok(())2095 }1986 }2096 1987 2102 match mode {1993 match mode {2103 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1994 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2104 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),1995 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2105 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), Error::<T>::TokenNotFound),2106 _ => ()1996 _ => ()2107 };1997 };2108 1998 2164 CreatedCollectionCount::put(next_id);2054 CreatedCollectionCount::put(next_id);2165 }2055 }216620562167 fn init_nft_token(item: &NftItemType<T::AccountId>) {2057 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2168 let current_index = <ItemListIndex>::get(item.collection)2058 let current_index = <ItemListIndex>::get(collection_id)2169 .checked_add(1)2059 .checked_add(1)2170 .unwrap();2060 .unwrap();217120612172 let item_owner = item.owner.clone();2062 let item_owner = item.owner.clone();2173 let collection_id = item.collection.clone();2174 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();2063 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();217520642176 <ItemListIndex>::insert(collection_id, current_index);2065 <ItemListIndex>::insert(collection_id, current_index);2182 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2071 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2183 }2072 }218420732185 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {2074 fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2186 let current_index = <ItemListIndex>::get(item.collection)2075 let current_index = <ItemListIndex>::get(collection_id)2187 .checked_add(1)2076 .checked_add(1)2188 .unwrap();2077 .unwrap();2189 let owner = item.owner.clone();219020782191 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2079 Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();219220802193 <ItemListIndex>::insert(item.collection, current_index);2081 <ItemListIndex>::insert(collection_id, current_index);219420822195 // Update balance2083 // Update balance2196 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2084 let new_balance = <Balance<T>>::get(collection_id, owner)2197 .checked_add(item.value)2085 .checked_add(item.value)2198 .unwrap();2086 .unwrap();2199 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2087 <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2200 }2088 }220120892202 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {2090 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2203 let current_index = <ItemListIndex>::get(item.collection)2091 let current_index = <ItemListIndex>::get(collection_id)2204 .checked_add(1)2092 .checked_add(1)2205 .unwrap();2093 .unwrap();220620942207 let value = item.owner.first().unwrap().fraction;2095 let value = item.owner.first().unwrap().fraction;2208 let owner = item.owner.first().unwrap().owner.clone();2096 let owner = item.owner.first().unwrap().owner.clone();220920972210 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2098 Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();221120992212 <ItemListIndex>::insert(item.collection, current_index);2100 <ItemListIndex>::insert(collection_id, current_index);221321012214 // Update balance2102 // Update balance2215 let new_balance = <Balance<T>>::get(item.collection, owner.clone())2103 let new_balance = <Balance<T>>::get(collection_id, owner.clone())2216 .checked_add(value)2104 .checked_add(value)2217 .unwrap();2105 .unwrap();2218 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);2106 <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2219 }2107 }222021082221 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {2109 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: T::AccountId) -> DispatchResult {2386 // };2274 // };2387 let fee = Self::traditional_fee(len, info, tip);2275 let fee = Self::traditional_fee(len, info, tip);238822762277 // Only mess with balances if fee is not zero.2278 if fee.is_zero() {2279 return Ok((fee, None));2280 }22812389 // Determine who is paying transaction fee based on ecnomic model2282 // Determine who is paying transaction fee based on ecnomic model2390 // Parse call to extract collection ID and access collection sponsor2283 // Parse call to extract collection ID and access collection sponsor2391 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2284 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2392 Some(Call::create_item(collection_id, _owner, _properties)) => {2285 Some(Call::create_item(collection_id, _owner, _properties)) => {239322862394 // check free create limit2287 // check free create limit2395 if <Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)2288 if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2289 (<Collection<T>>::get(collection_id).sponsor_confirmed)2396 {2290 {2397 <Collection<T>>::get(collection_id).sponsor2291 <Collection<T>>::get(collection_id).sponsor2398 } else {2292 } else {2399 T::AccountId::default()2293 T::AccountId::default()2400 }2294 }2401 }2295 }2402 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2296 Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2403 2297 2404 let _collection_limits = <Collection<T>>::get(collection_id).limits;2298 let mut sponsor_transfer = false;2405 let _collection_mode = <Collection<T>>::get(collection_id).mode;2299 if <Collection<T>>::get(collection_id).sponsor_confirmed {240623002407 // sponsor timeout2301 let collection_limits = <Collection<T>>::get(collection_id).limits;2302 let collection_mode = <Collection<T>>::get(collection_id).mode;2303 2304 // sponsor timeout2408 let sponsor_transfer = match _collection_mode {2305 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2306 sponsor_transfer = match collection_mode {2409 CollectionMode::NFT => {2307 CollectionMode::NFT => {2308 2309 // get correct limit2310 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2311 collection_limits.sponsor_transfer_timeout2312 } else {2313 ChainLimit::get().nft_sponsor_transfer_timeout2314 };2315 2316 let mut sponsored = true;2317 if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2318 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2319 let limit_time = last_tx_block + limit.into();2320 if block_number <= limit_time {2321 sponsored = false;2322 }2323 }2324 if sponsored {2325 <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2326 }241023272411 // get correct limit2328 sponsored2412 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2413 _collection_limits.sponsor_transfer_timeout2414 } else {2415 ChainLimit::get().nft_sponsor_transfer_timeout2416 };24172418 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2419 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2420 let limit_time = basket + limit.into();2421 if block_number >= limit_time {2422 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2423 true2424 }2329 }2425 else {2330 CollectionMode::Fungible(_) => {2426 false2331 2427 }2428 }2429 CollectionMode::Fungible(_) => {24302431 // get correct limit2332 // get correct limit2432 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2333 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2433 _collection_limits.sponsor_transfer_timeout2334 collection_limits.sponsor_transfer_timeout2434 } else {2335 } else {2435 ChainLimit::get().fungible_sponsor_transfer_timeout2336 ChainLimit::get().fungible_sponsor_transfer_timeout2436 };2337 };24372438 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2338 2439 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2339 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2440 if basket.iter().any(|i| i.address == _new_owner.clone())2340 let mut sponsored = true;2441 {2442 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2341 if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2443 let limit_time = item.start_block + limit.into();2342 let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2444 if block_number >= limit_time {2343 let limit_time = last_tx_block + limit.into();2445 basket.retain(|x| x.address == item.address);2446 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2344 if block_number <= limit_time {2447 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2345 sponsored = false;2448 true2346 }2449 }2347 }2450 else {2348 if sponsored {2451 false2349 <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2452 }2350 }23512352 sponsored2453 }2353 }2454 else {2354 CollectionMode::ReFungible(_) => {2455 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2355 2356 // get correct limit2357 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2358 collection_limits.sponsor_transfer_timeout2359 } else {2360 ChainLimit::get().refungible_sponsor_transfer_timeout2361 };2362 2363 let mut sponsored = true;2364 if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2365 let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2366 let limit_time = last_tx_block + limit.into();2456 true2367 if block_number <= limit_time {2457 }2368 sponsored = false;2369 }2458 }2370 }2459 CollectionMode::ReFungible(_) => {2371 if sponsored {2372 <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2373 }246023742461 // get correct limit2375 sponsored2462 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {2463 _collection_limits.sponsor_transfer_timeout2464 } else {2465 ChainLimit::get().refungible_sponsor_transfer_timeout2466 };24672468 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2469 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2470 let limit_time = basket + limit.into();2471 if block_number >= limit_time {2472 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2473 true2474 } else {2475 false2476 }2376 }2477 }2377 _ => {2478 _ => {2378 false2479 false2379 },2480 },2380 };2481 };2381 }248223822483 if !sponsor_transfer {2383 if !sponsor_transfer {2484 T::AccountId::default()2384 T::AccountId::default()2555 let mut who_pays_fee: T::AccountId = sponsor.clone();2455 let mut who_pays_fee: T::AccountId = sponsor.clone();2556 if sponsor == T::AccountId::default() {2456 if sponsor == T::AccountId::default() {2557 who_pays_fee = who.clone();2457 who_pays_fee = who.clone();2558 }25592560 // Only mess with balances if fee is not zero.2561 if fee.is_zero() {2562 return Ok((fee, None));2563 }2458 }256424592565 match <T as transaction_payment::Trait>::Currency::withdraw(2460 match <T as transaction_payment::Trait>::Currency::withdraw(runtime_types.jsondiffbeforeafterboth42 "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",tests/src/confirmSponsorship.test.tsdiffbeforeafterbothno changes
tests/src/createItem.test.tsdiffbeforeafterboth1import { 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('0', '0', '0', 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('0', '0', '0', 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('0', '0', '0', createMode);
25 await createItemExpectSuccess(newCollectionID, createMode, '//Alice');
32 await createItemExpectSuccess(alice, newCollectionID, createMode);
26 });
33 });
27});
34});
2835tests/src/createMultipleItems.test.tsdiffbeforeafterboth111112const idCollection = 12;12const idCollection = 12;131314describe('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);tests/src/destroyCollection.test.tsdiffbeforeafterboth1import 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'; 879chai.use(chaiAsPromised);8chai.use(chaiAsPromised);10const expect = chai.expect;9const expect = chai.expect;1112function 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}2223async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {24 await usingApi(async (api) => {25 // Run the DestroyCollection transaction26 const alicePrivateKey = privateKey(senderSeed);27 const tx = api.tx.nft.destroyCollection(collectionId);28 const events = await submitTransactionAsync(alicePrivateKey, tx);29 const result = getDestroyResult(events);3031 // Get the collection 32 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();3334 // What to expect35 expect(result).to.be.true;36 expect(collection).to.be.not.null;37 expect(collection.Owner).to.be.equal(nullPublicKey);38 });39}4041async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {42 await usingApi(async (api) => {43 // Run the DestroyCollection transaction44 const alicePrivateKey = privateKey(senderSeed);45 const tx = api.tx.nft.destroyCollection(collectionId);46 const events = await submitTransactionAsync(alicePrivateKey, tx);47 const result = getDestroyResult(events);4849 // What to expect50 expect(result).to.be.false;51 });52}531054describe('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 () => {tests/src/setCollectionSponsor.test.tsdiffbeforeafterbothno changes
tests/src/util/helpers.tsdiffbeforeafterboth9import { 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 } 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';161817chai.use(chaiAsPromised);19chai.use(chaiAsPromised);18const expect = chai.expect;20const expect = chai.expect;26 collectionId: number28 collectionId: number27};29};3031type CreateItemResult = {32 success: boolean,33 collectionId: number,34 itemId: number35};283629export 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}6768function 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 itemId85 }86 return result;87}598860export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {89export async function createCollectionExpectSuccess(name: string, description: string, tokenPrefix: string, mode: string): Promise<number> {61 let collectionId: number = 0;90 let collectionId: number = 0;123 return unused; 152 return unused; 124}153}154155function getDestroyResult(events: EventRecord[]): boolean {156 let success: boolean = false;157 events.forEach(({ phase, event: { data, method, section } }) => {158 // console.log(` ${phase}: ${section}.${method}:: ${data}`);159 if (method == 'ExtrinsicSuccess') {160 success = true;161 }162 });163 return success;164}165166export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {167 await usingApi(async (api) => {168 // Run the DestroyCollection transaction169 const alicePrivateKey = privateKey(senderSeed);170 const tx = api.tx.nft.destroyCollection(collectionId);171 const events = await submitTransactionAsync(alicePrivateKey, tx);172 const result = getDestroyResult(events);173174 // What to expect175 expect(result).to.be.false;176 });177}178179export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {180 await usingApi(async (api) => {181 // Run the DestroyCollection transaction182 const alicePrivateKey = privateKey(senderSeed);183 const tx = api.tx.nft.destroyCollection(collectionId);184 const events = await submitTransactionAsync(alicePrivateKey, tx);185 const result = getDestroyResult(events);186187 // Get the collection 188 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();189190 // What to expect191 expect(result).to.be.true;192 expect(collection).to.be.not.null;193 expect(collection.Owner).to.be.equal(nullPublicKey);194 });195}196197export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {198 await usingApi(async (api) => {199200 // Run the transaction201 const alicePrivateKey = privateKey('//Alice');202 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);203 const events = await submitTransactionAsync(alicePrivateKey, tx);204 const result = getGenericResult(events);205206 // Get the collection 207 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();208209 // What to expect210 expect(result.success).to.be.true;211 expect(collection.Sponsor.toString()).to.be.equal(sponsor.toString());212 expect(collection.SponsorConfirmed).to.be.false;213 });214}215216export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {217 await usingApi(async (api) => {218219 // Run the transaction220 const alicePrivateKey = privateKey(senderSeed);221 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);222 const events = await submitTransactionAsync(alicePrivateKey, tx);223 const result = getGenericResult(events);224225 // What to expect226 expect(result.success).to.be.false;227 });228}229230export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {231 await usingApi(async (api) => {232233 // Run the transaction234 const sender = privateKey(senderSeed);235 const tx = api.tx.nft.confirmSponsorship(collectionId);236 const events = await submitTransactionAsync(sender, tx);237 const result = getGenericResult(events);238239 // Get the collection 240 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();241242 // What to expect243 expect(result.success).to.be.true;244 expect(collection.Sponsor).to.be.equal(sender.address);245 expect(collection.SponsorConfirmed).to.be.true;246 });247}248249export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {250 await usingApi(async (api) => {251252 // Run the transaction253 const sender = privateKey(senderSeed);254 const tx = api.tx.nft.confirmSponsorship(collectionId);255 const events = await submitTransactionAsync(sender, tx);256 const result = getGenericResult(events);257258 // What to expect259 expect(result.success).to.be.false;260 });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};125275126export async function createItemExpectSuccess(collectionId: number, createMode: string, senderSeed: string = '//Alice') {276export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {277 let newItemId: number = 0;127 await usingApi(async (api) => {278 await usingApi(async (api) => {128129 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());279 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());130131 const sender = privateKey(senderSeed);280 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON(); 132 const tx = api.tx.nft.createItem(collectionId, sender.address, createMode);281 const AItemBalance = new BigNumber(Aitem.Value);282283 if (owner === '') owner = sender.address;284285 let tx;286 if (createMode == 'Fungible') {287 let createData = {fungible: {value: 10}};288 tx = api.tx.nft.createItem(collectionId, owner, createData);289 }290 else {291 tx = api.tx.nft.createItem(collectionId, owner, createMode);292 }133 const events = await submitTransactionAsync(sender, tx);293 const events = await submitTransactionAsync(sender, tx);134 const result = getGenericResult(events);294 const result = getCreateItemResult(events);135 295136 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());296 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());297 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON(); 298 const BItemBalance = new BigNumber(Bitem.Value);137299138 // What to expect300 // What to expect139 expect(result.success).to.be.true;301 expect(result.success).to.be.true;302 if (createMode == 'Fungible') {303 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);304 }305 else {140 expect(BItemCount).to.be.equal(AItemCount+1);306 expect(BItemCount).to.be.equal(AItemCount+1);307 }308 expect(collectionId).to.be.equal(result.collectionId);309 expect(BItemCount).to.be.equal(result.itemId);310 newItemId = result.itemId;141 });311 });312 return newItemId;142}313}314315export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {316 await usingApi(async (api) => {317318 // Run the transaction319 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');320 const events = await submitTransactionAsync(sender, tx);321 const result = getGenericResult(events);322323 // Get the collection 324 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();325326 // What to expect327 expect(result.success).to.be.true;328 expect(collection.Access).to.be.equal('WhiteList');329 });330}331332export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {333 await usingApi(async (api) => {334335 // Run the transaction336 const tx = api.tx.nft.setMintPermission(collectionId, true);337 const events = await submitTransactionAsync(sender, tx);338 const result = getGenericResult(events);339340 // Get the collection 341 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();342343 // What to expect344 expect(result.success).to.be.true;345 expect(collection.MintMode).to.be.equal(true);346 });347}348349export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {350 await usingApi(async (api) => {351352 // Run the transaction353 const tx = api.tx.nft.addToWhiteList(collectionId, address);354 const events = await submitTransactionAsync(sender, tx);355 const result = getGenericResult(events);356357 // Get the collection 358 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();359360 // What to expect361 expect(result.success).to.be.true;362 expect(collection.MintMode).to.be.equal(true);363 });364}143365366