git.delta.rocks / unique-network / refs/commits / cd2930267fbc

difftreelog

Merge pull request #50 from usetech-llc/feature/NFTPAR-250

str-mv2020-12-25parents: #d430fcf #b526a8b.patch.diff
in: master
NFTPAR-250 Tests for collection sponsoring

9 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
193 offchain_schema: vec![],193 offchain_schema: vec![],
194 schema_version: SchemaVersion::default(),194 schema_version: SchemaVersion::default(),
195 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),195 sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
196 unconfirmed_sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),196 sponsor_confirmed: true,
197 const_on_chain_schema: vec![],197 const_on_chain_schema: vec![],
198 variable_on_chain_schema: vec![],198 variable_on_chain_schema: vec![],
199 limits: CollectionLimits::default()199 limits: CollectionLimits::default()
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
136 pub offchain_schema: Vec<u8>,136 pub offchain_schema: Vec<u8>,
137 pub schema_version: SchemaVersion,137 pub schema_version: SchemaVersion,
138 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender138 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender
139 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship139 pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.
140 pub limits: CollectionLimits, // Collection private restrictions 140 pub limits: CollectionLimits, // Collection private restrictions
141 pub variable_on_chain_schema: Vec<u8>, //141 pub variable_on_chain_schema: Vec<u8>, //
142 pub const_on_chain_schema: Vec<u8>, //142 pub const_on_chain_schema: Vec<u8>, //
145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]145#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]146#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
147pub struct NftItemType<AccountId> {147pub struct NftItemType<AccountId> {
148 pub collection: CollectionId,
149 pub owner: AccountId,148 pub owner: AccountId,
150 pub const_data: Vec<u8>,149 pub const_data: Vec<u8>,
151 pub variable_data: Vec<u8>,150 pub variable_data: Vec<u8>,
152}151}
153152
154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
156pub struct FungibleItemType<AccountId> {155pub struct FungibleItemType {
157 pub collection: CollectionId,
158 pub owner: AccountId,
159 pub value: u128,156 pub value: u128,
160}157}
161158
162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]159#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]160#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
164pub struct ReFungibleItemType<AccountId> {161pub struct ReFungibleItemType<AccountId> {
165 pub collection: CollectionId,
166 pub owner: Vec<Ownership<AccountId>>,162 pub owner: Vec<Ownership<AccountId>>,
167 pub const_data: Vec<u8>,163 pub const_data: Vec<u8>,
168 pub variable_data: Vec<u8>,164 pub variable_data: Vec<u8>,
169}165}
170166
171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
173pub struct ApprovePermissions<AccountId> {169// pub struct VestingItem<AccountId, Moment> {
174 pub approved: AccountId,170// pub sender: AccountId,
175 pub amount: u128,171// pub recipient: AccountId,
172// pub collection_id: CollectionId,
173// pub item_id: TokenId,
174// pub amount: u64,
175// pub vesting_date: Moment,
176}176// }
177177
178#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
180pub struct VestingItem<AccountId, Moment> {
181 pub sender: AccountId,
182 pub recipient: AccountId,
183 pub collection_id: CollectionId,
184 pub item_id: TokenId,
185 pub amount: u64,
186 pub vesting_date: Moment,
187}
188
189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
191pub struct BasketItem<AccountId, BlockNumber> {
192 pub address: AccountId,
193 pub start_block: BlockNumber,
194}
195
196#[derive(Encode, Decode, Debug, Clone, PartialEq)]178#[derive(Encode, Decode, Debug, Clone, PartialEq)]
197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]179#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
198pub struct CollectionLimits {180pub struct CollectionLimits {
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}
267250
268#[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 map
418 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;
419402
420 /// second parameter: item id + owner account id403 /// second parameter: item id + owner account id + spender account id
421 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;
422405
423 /// Item collections406 /// Item collections
424 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>;
427410
428 /// Index list411 /// Index list
429 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>;
430413
431 /// Tokens transfer baskets414 /// Tokens transfer baskets
432 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;
435418
436 // Contract Sponsorship and Ownership419 // Contract Sponsorship and Ownership
448 <Module<T>>::init_collection(_c);431 <Module<T>>::init_collection(_c);
449 }432 }
450433
451 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 }
454437
455 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 }
458441
459 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)?;
625608
626 <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);
854837
855 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);
857841
858 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);
873857
874 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);
876860
877 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);
880863
881 Ok(())864 Ok(())
900 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);883 ensure!(sender == target_collection.owner, Error::<T>::NoPermission);
901884
902 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);
904888
905 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 {
1004988
1005 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.mode
1019 {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.mode
1075 {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 {
11021086
1103 let sender = ensure_signed(origin)?;1087 let sender = ensure_signed(origin)?;
11041088
11101094
1111 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 }
11151099
1116 // amount param stub1100 let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));
1117 let amount = 100000000;
1118
1119 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));
1120 if list_exists {1101 let mut allowance: u128 = amount;
1121
1122 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));
1123 let item_contains = list.iter().any(|i| i.approved == approved);
1124
1125 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 {
1130
1131 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);
11351106
1136 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;
11631134
1164 // Check approve1135 // Check approval
1165 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 }
11741142
1175 let target_collection = <Collection<T>>::get(collection_id);1143 let target_collection = <Collection<T>>::get(collection_id);
11791147
1180 // 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);
11831151
1184 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 }
11881156
1189 // remove approve1157 // Reduce approval by transferred amount or remove if remaining approval drops to 0
1190 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 }
11931164
1194
1195 match target_collection.mode1165 match target_collection.mode
1196 {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_data
1621 };1590 };
16221591
1623 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 };
1631
1632 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});
16381601
1639 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_data
1644 };1606 };
16451607
1646 Self::add_refungible_item(item)?;1608 Self::add_refungible_item(collection_id, item)?;
1647 }1609 }
1648 };1610 };
16491611
1653 Ok(())1615 Ok(())
1654 }1616 }
16551617
1656 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();
16621619
1663 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 }
16641625
1665 <ItemListIndex>::insert(item.collection, current_index);1626 // Mint
1627 let item = FungibleItemType {
1628 value: balance + value
1629 };
1666 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);1630 <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);
16671631
1668 // Add current block
1669 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();
1670 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);
1671
1672 // Update balance1632 // Update balance
1673 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);
16771637
1678 Ok(())1638 Ok(())
1679 }1639 }
16801640
1681 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();
16861646
1687 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();
16891649
1690 Self::add_token_index(item.collection, current_index, owner.clone())?;1650 Self::add_token_index(collection_id, current_index, owner.clone())?;
16911651
1692 <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);
16941654
1695 // Add current block
1696 let block_number: T::BlockNumber = 0.into();
1697 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);
1698
1699 // Update balance1655 // Update balance
1700 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);
17041660
1705 Ok(())1661 Ok(())
1706 }1662 }
17071663
1708 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)?;
17121668
1713 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())?;
17161671
1717 <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);
17191674
1720 // Add current block
1721 let block_number: T::BlockNumber = 0.into();
1722 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);
1723
1724 // Update balance1675 // Update balance
1725 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())?;
17501701
1751 // remove approve list
1752 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));
1753
1754 // update balance1702 // update balance
1755 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())?;
17721720
1773 // remove approve list
1774 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));
1775
1776 // update balance1721 // update balance
1777 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 }
17851730
1786 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>::TokenNotFound
1790 );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);
17931738
1794 // remove approve list
1795 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));
1796
1797 // update balance1739 // update balance
1798 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);
18021744
1803 <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 }
18041752
1805 Ok(())1753 Ok(())
1806 }1754 }
1861 <NftItemList<T>>::get(collection_id, item_id).owner == subject1809 <NftItemList<T>>::get(collection_id, item_id).owner == subject
1862 }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)
18821830
1883 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>::TokenNotFound
1893 );1840 );
18941841
1895 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);
18971844
1898 ensure!(amount >= value, Error::<T>::TokenValueTooLow);1845 // Send balance to recipient (updates balanceOf of recipient)
1846 Self::add_fungible_item(collection_id, recipient, value)?;
18991847
1900 // update balance1848 // update balanceOf of sender
1901 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);
19051850
1906 let mut new_owner_account_id = 0;1851 // Reduce or remove sender
1907 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 owner
1915 // new owner do not have account
1916 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);
1919
1920 // update balance
1921 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);
1925
1926 // update index collection
1927 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;
1931
1932 // separate amount
1933 if new_owner_account_id > 0 {
1934 // new owner has account
1935 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);
1936 item.value += value;
1937
1938 // update balance
1939 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);
1943
1944 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);
1945 } else {
1946 // new owner do not have account
1947 let item = FungibleItemType {
1948 collection: collection_id,
1949 owner: new_owner.clone(),
1950 value
1951 };
1952
1953 Self::add_fungible_item(item)?;
1954 }
1955
1956 if amount == value {
1957 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;
1958
1959 // remove approve list
1960 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));
1961 <FungibleItemList<T>>::remove(collection_id, item_id);
1962 }
1963
1964 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);
1965 }1858 }
19661859
1967 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);
19981891
1999 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);
20031896
2004 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);
20781971
2079 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);
20831976
2084 // change owner1977 // change owner
2085 let old_owner = item.owner.clone();1978 let old_owner = item.owner.clone();
2089 // update index collection1982 // update index collection
2090 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())?;
20911984
2092 // reset approved list
2093 <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 }
21662056
2167 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();
21712061
2172 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();
21752064
2176 <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 }
21842073
2185 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();
21902078
2191 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2079 Self::add_token_index(collection_id, current_index, (*owner).clone()).unwrap();
21922080
2193 <ItemListIndex>::insert(item.collection, current_index);2081 <ItemListIndex>::insert(collection_id, current_index);
21942082
2195 // Update balance2083 // Update balance
2196 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 }
22012089
2202 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();
22062094
2207 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();
22092097
2210 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();2098 Self::add_token_index(collection_id, current_index, owner.clone()).unwrap();
22112099
2212 <ItemListIndex>::insert(item.collection, current_index);2100 <ItemListIndex>::insert(collection_id, current_index);
22132101
2214 // Update balance2102 // Update balance
2215 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 }
22202108
2221 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);
23882276
2277 // Only mess with balances if fee is not zero.
2278 if fee.is_zero() {
2279 return Ok((fee, None));
2280 }
2281
2389 // Determine who is paying transaction fee based on ecnomic model2282 // Determine who is paying transaction fee based on ecnomic model
2390 // Parse call to extract collection ID and access collection sponsor2283 // Parse call to extract collection ID and access collection sponsor
2391 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)) => {
23932286
2394 // check free create limit2287 // check free create limit
2395 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).sponsor
2398 } 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 {
24062300
2407 // sponsor timeout2301 let collection_limits = <Collection<T>>::get(collection_id).limits;
2302 let collection_mode = <Collection<T>>::get(collection_id).mode;
2303
2304 // sponsor timeout
2408 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 limit
2310 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
2311 collection_limits.sponsor_transfer_timeout
2312 } else {
2313 ChainLimit::get().nft_sponsor_transfer_timeout
2314 };
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 }
24102327
2411 // get correct limit2328 sponsored
2412 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2413 _collection_limits.sponsor_transfer_timeout
2414 } else {
2415 ChainLimit::get().nft_sponsor_transfer_timeout
2416 };
2417
2418 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 true
2424 }2329 }
2425 else {2330 CollectionMode::Fungible(_) => {
2426 false2331
2427 }
2428 }
2429 CollectionMode::Fungible(_) => {
2430
2431 // get correct limit2332 // get correct limit
2432 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_timeout
2434 } else {2335 } else {
2435 ChainLimit::get().fungible_sponsor_transfer_timeout2336 ChainLimit::get().fungible_sponsor_transfer_timeout
2436 };2337 };
2437
2438 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 }
2351
2352 sponsored
2453 }2353 }
2454 else {2354 CollectionMode::ReFungible(_) => {
2455 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2355
2356 // get correct limit
2357 let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
2358 collection_limits.sponsor_transfer_timeout
2359 } else {
2360 ChainLimit::get().refungible_sponsor_transfer_timeout
2361 };
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 }
24602374
2461 // get correct limit2375 sponsored
2462 let limit: u32 = if _collection_limits.sponsor_transfer_timeout > 0 {
2463 _collection_limits.sponsor_transfer_timeout
2464 } else {
2465 ChainLimit::get().refungible_sponsor_transfer_timeout
2466 };
2467
2468 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 true
2474 } else {
2475 false
2476 }2376 }
2477 }2377 _ => {
2478 _ => {2378 false
2479 false2379 },
2480 },2380 };
2481 };2381 }
24822382
2483 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 }
2559
2560 // Only mess with balances if fee is not zero.
2561 if fee.is_zero() {
2562 return Ok((fee, None));
2563 }2458 }
25642459
2565 match <T as transaction_payment::Trait>::Currency::withdraw(2460 match <T as transaction_payment::Trait>::Currency::withdraw(
modifiedruntime_types.jsondiffbeforeafterboth
42 "Fraction": "u128"42 "Fraction": "u128"
43 },43 },
44 "FungibleItemType": {44 "FungibleItemType": {
45 "Collection": "CollectionId",
46 "Owner": "AccountId",
47 "Value": "u128"45 "Value": "u128"
48 },46 },
49 "NftItemType": {47 "NftItemType": {
50 "Collection": "CollectionId",
51 "Owner": "AccountId",48 "Owner": "AccountId",
52 "ConstData": "Vec<u8>",49 "ConstData": "Vec<u8>",
53 "VariableData": "Vec<u8>"50 "VariableData": "Vec<u8>"
54 },51 },
55 "ReFungibleItemType": {52 "ReFungibleItemType": {
56 "Collection": "CollectionId",
57 "Owner": "Vec<Ownership<AccountId>>",53 "Owner": "Vec<Ownership<AccountId>>",
58 "ConstData": "Vec<u8>",54 "ConstData": "Vec<u8>",
59 "VariableData": "Vec<u8>"55 "VariableData": "Vec<u8>"
70 "OffchainSchema": "Vec<u8>",66 "OffchainSchema": "Vec<u8>",
71 "SchemaVersion": "SchemaVersion",67 "SchemaVersion": "SchemaVersion",
72 "Sponsor": "AccountId",68 "Sponsor": "AccountId",
73 "UnconfirmedSponsor": "AccountId",69 "SponsorConfirmed": "bool",
74 "Limits": "CollectionLimits",70 "Limits": "CollectionLimits",
75 "VariableOnChainSchema": "Vec<u8>",71 "VariableOnChainSchema": "Vec<u8>",
76 "ConstOnChainSchema": "Vec<u8>"72 "ConstOnChainSchema": "Vec<u8>"
77 },73 },
78 "ApprovePermissions": {
79 "Approved": "AccountId",
80 "Amount": "u128"
81 },
82 "RawData": "Vec<u8>",74 "RawData": "Vec<u8>",
83 "Address": "AccountId",75 "Address": "AccountId",
84 "LookupSource": "AccountId",76 "LookupSource": "AccountId",
88 "variable_data": "Vec<u8>" 80 "variable_data": "Vec<u8>"
89 },81 },
90 "CreateFungibleData": {},82 "CreateFungibleData": {
83 "value": "u128"
84 },
91 "CreateReFungibleData": {85 "CreateReFungibleData": {
92 "const_data": "Vec<u8>",86 "const_data": "Vec<u8>",
107 },101 },
108 "CollectionId": "u32",102 "CollectionId": "u32",
109 "TokenId": "u32",103 "TokenId": "u32",
110 "BasketItem": {
111 "Address": "AccountId",
112 "start_block": "BlockNumber"
113 },
114 "ChainLimits": {104 "ChainLimits": {
115 "collection_numbers_limit": "u32",105 "collection_numbers_limit": "u32",
116 "account_token_ownership_limit": "u32",106 "account_token_ownership_limit": "u32",
addedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth

no changes

modifiedtests/src/createItem.test.tsdiffbeforeafterboth
1import { assert } from 'chai';
2import { alicesPublicKey } from './accounts';
3import privateKey from './substrate/privateKey';
4import { default as usingApi } from './substrate/substrate-api'; 1import { default as usingApi } from './substrate/substrate-api';
2import { Keyring } from "@polkadot/api";
5import waitNewBlocks from './substrate/wait-new-blocks'; 3import { IKeyringPair } from "@polkadot/types/types";
6import { 4import {
7 createCollectionExpectSuccess, 5 createCollectionExpectSuccess,
8 createItemExpectSuccess 6 createItemExpectSuccess
9} from './util/helpers'; 7} from './util/helpers';
10 8
9let alice: IKeyringPair;
10
11describe('integration test: ext. createItem():', () => { 11describe('integration test: ext. createItem():', () => {
12 before(async () => {
13 await usingApi(async (api) => {
14 const keyring = new Keyring({ type: 'sr25519' });
15 alice = keyring.addFromUri(`//Alice`);
16 });
17 });
18
12 it('Create new item in NFT collection', async () => { 19 it('Create new item in NFT collection', async () => {
13 const createMode = 'NFT'; 20 const createMode = 'NFT';
14 const newCollectionID = await createCollectionExpectSuccess('0', '0', '0', createMode); 21 const newCollectionID = await createCollectionExpectSuccess('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});
2835
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
1111
12const idCollection = 12;12const idCollection = 12;
1313
14describe('integration test: ext. createMultipleItems():', () => {14describe.skip('integration test: ext. createMultipleItems():', () => {
15 it('Create two NFT tokens in active NFT collection', async () => {15 it('Create two NFT tokens in active NFT collection', async () => {
16 await usingApi(async (api) => {16 await usingApi(async (api) => {
17 const AitemListIndex = await api.query.nft.itemListIndex(idCollection);17 const AitemListIndex = await api.query.nft.itemListIndex(idCollection);
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
1import chai from 'chai';1import chai from 'chai';
2import chaiAsPromised from 'chai-as-promised';2import chaiAsPromised from 'chai-as-promised';
3import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";3import { default as usingApi, submitTransactionAsync } from "./substrate/substrate-api";
4import { createCollectionExpectSuccess, createCollectionExpectFailure } from "./util/helpers";4import { createCollectionExpectSuccess, createCollectionExpectFailure, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers";
5import type { AccountId, EventRecord } from '@polkadot/types/interfaces';5import type { AccountId, EventRecord } from '@polkadot/types/interfaces';
6import privateKey from './substrate/privateKey';6import privateKey from './substrate/privateKey';
7import { nullPublicKey } from './accounts';
87
9chai.use(chaiAsPromised);8chai.use(chaiAsPromised);
10const expect = chai.expect;9const expect = chai.expect;
11
12function getDestroyResult(events: EventRecord[]): boolean {
13 let success: boolean = false;
14 events.forEach(({ phase, event: { data, method, section } }) => {
15 // console.log(` ${phase}: ${section}.${method}:: ${data}`);
16 if (method == 'ExtrinsicSuccess') {
17 success = true;
18 }
19 });
20 return success;
21}
22
23async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
24 await usingApi(async (api) => {
25 // Run the DestroyCollection transaction
26 const alicePrivateKey = privateKey(senderSeed);
27 const tx = api.tx.nft.destroyCollection(collectionId);
28 const events = await submitTransactionAsync(alicePrivateKey, tx);
29 const result = getDestroyResult(events);
30
31 // Get the collection
32 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
33
34 // What to expect
35 expect(result).to.be.true;
36 expect(collection).to.be.not.null;
37 expect(collection.Owner).to.be.equal(nullPublicKey);
38 });
39}
40
41async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
42 await usingApi(async (api) => {
43 // Run the DestroyCollection transaction
44 const alicePrivateKey = privateKey(senderSeed);
45 const tx = api.tx.nft.destroyCollection(collectionId);
46 const events = await submitTransactionAsync(alicePrivateKey, tx);
47 const result = getDestroyResult(events);
48
49 // What to expect
50 expect(result).to.be.false;
51 });
52}
5310
54describe('integration test: ext. destroyCollection():', () => {11describe('integration test: ext. destroyCollection():', () => {
55 it('NFT collection can be destroyed', async () => {12 it('NFT collection can be destroyed', async () => {
addedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth

no changes

modifiedtests/src/util/helpers.tsdiffbeforeafterboth
9import { ApiPromise, Keyring } from "@polkadot/api";9import { ApiPromise, Keyring } from "@polkadot/api";
10import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api";10import { default as usingApi, submitTransactionAsync } from "../substrate/substrate-api";
11import privateKey from '../substrate/privateKey';11import privateKey from '../substrate/privateKey';
12import { alicesPublicKey } from "../accounts";12import { alicesPublicKey, nullPublicKey } from "../accounts";
13import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';13import { strToUTF16, utf16ToStr, hexToStr } from '../util/util';
14import { IKeyringPair } from "@polkadot/types/types";14import { IKeyringPair } from "@polkadot/types/types";
15import { BigNumber } from 'bignumber.js';15import { BigNumber } from 'bignumber.js';
16import { Struct, Enum } from '@polkadot/types/codec';
17import { u128 } from '@polkadot/types/primitive';
1618
17chai.use(chaiAsPromised);19chai.use(chaiAsPromised);
18const expect = chai.expect;20const expect = chai.expect;
26 collectionId: number28 collectionId: number
27};29};
30
31type CreateItemResult = {
32 success: boolean,
33 collectionId: number,
34 itemId: number
35};
2836
29export function getGenericResult(events: EventRecord[]): GenericResult {37export function getGenericResult(events: EventRecord[]): GenericResult {
30 let result: GenericResult = {38 let result: GenericResult = {
57 return result;65 return result;
58}66}
67
68function getCreateItemResult(events: EventRecord[]): CreateItemResult {
69 let success = false;
70 let collectionId: number = 0;
71 let itemId: number = 0;
72 events.forEach(({ phase, event: { data, method, section } }) => {
73 // console.log(` ${phase}: ${section}.${method}:: ${data}`);
74 if (method == 'ExtrinsicSuccess') {
75 success = true;
76 } else if ((section == 'nft') && (method == 'ItemCreated')) {
77 collectionId = parseInt(data[0].toString());
78 itemId = parseInt(data[1].toString());
79 }
80 });
81 let result: CreateItemResult = {
82 success,
83 collectionId,
84 itemId
85 }
86 return result;
87}
5988
60export 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}
154
155function 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}
165
166export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
167 await usingApi(async (api) => {
168 // Run the DestroyCollection transaction
169 const alicePrivateKey = privateKey(senderSeed);
170 const tx = api.tx.nft.destroyCollection(collectionId);
171 const events = await submitTransactionAsync(alicePrivateKey, tx);
172 const result = getDestroyResult(events);
173
174 // What to expect
175 expect(result).to.be.false;
176 });
177}
178
179export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
180 await usingApi(async (api) => {
181 // Run the DestroyCollection transaction
182 const alicePrivateKey = privateKey(senderSeed);
183 const tx = api.tx.nft.destroyCollection(collectionId);
184 const events = await submitTransactionAsync(alicePrivateKey, tx);
185 const result = getDestroyResult(events);
186
187 // Get the collection
188 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
189
190 // What to expect
191 expect(result).to.be.true;
192 expect(collection).to.be.not.null;
193 expect(collection.Owner).to.be.equal(nullPublicKey);
194 });
195}
196
197export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {
198 await usingApi(async (api) => {
199
200 // Run the transaction
201 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);
205
206 // Get the collection
207 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
208
209 // What to expect
210 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}
215
216export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {
217 await usingApi(async (api) => {
218
219 // Run the transaction
220 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);
224
225 // What to expect
226 expect(result.success).to.be.false;
227 });
228}
229
230export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {
231 await usingApi(async (api) => {
232
233 // Run the transaction
234 const sender = privateKey(senderSeed);
235 const tx = api.tx.nft.confirmSponsorship(collectionId);
236 const events = await submitTransactionAsync(sender, tx);
237 const result = getGenericResult(events);
238
239 // Get the collection
240 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
241
242 // What to expect
243 expect(result.success).to.be.true;
244 expect(collection.Sponsor).to.be.equal(sender.address);
245 expect(collection.SponsorConfirmed).to.be.true;
246 });
247}
248
249export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {
250 await usingApi(async (api) => {
251
252 // Run the transaction
253 const sender = privateKey(senderSeed);
254 const tx = api.tx.nft.confirmSponsorship(collectionId);
255 const events = await submitTransactionAsync(sender, tx);
256 const result = getGenericResult(events);
257
258 // What to expect
259 expect(result.success).to.be.false;
260 });
261}
262
263export interface CreateFungibleData extends Struct {
264 readonly value: u128;
265};
266
267export interface CreateReFungibleData extends Struct {};
268export interface CreateNftData extends Struct {};
269
270export interface CreateItemData extends Enum {
271 NFT: CreateNftData,
272 Fungible: CreateFungibleData,
273 ReFungible: CreateReFungibleData
274};
125275
126export 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) => {
128
129 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());279 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString());
130
131 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);
282
283 if (owner === '') owner = sender.address;
284
285 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 295
136 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);
137299
138 // What to expect300 // What to expect
139 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}
314
315export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {
316 await usingApi(async (api) => {
317
318 // Run the transaction
319 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'WhiteList');
320 const events = await submitTransactionAsync(sender, tx);
321 const result = getGenericResult(events);
322
323 // Get the collection
324 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
325
326 // What to expect
327 expect(result.success).to.be.true;
328 expect(collection.Access).to.be.equal('WhiteList');
329 });
330}
331
332export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {
333 await usingApi(async (api) => {
334
335 // Run the transaction
336 const tx = api.tx.nft.setMintPermission(collectionId, true);
337 const events = await submitTransactionAsync(sender, tx);
338 const result = getGenericResult(events);
339
340 // Get the collection
341 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
342
343 // What to expect
344 expect(result.success).to.be.true;
345 expect(collection.MintMode).to.be.equal(true);
346 });
347}
348
349export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {
350 await usingApi(async (api) => {
351
352 // Run the transaction
353 const tx = api.tx.nft.addToWhiteList(collectionId, address);
354 const events = await submitTransactionAsync(sender, tx);
355 const result = getGenericResult(events);
356
357 // Get the collection
358 const collection: any = (await api.query.nft.collection(collectionId)).toJSON();
359
360 // What to expect
361 expect(result.success).to.be.true;
362 expect(collection.MintMode).to.be.equal(true);
363 });
364}
143365
366