git.delta.rocks / unique-network / refs/commits / 9836c2113849

difftreelog

Merge branch 'develop' into feature/internal-audit

Igor Kozyrev2021-11-23parents: #6a168a1 #bba462e.patch.diff
in: master

57 files changed

modified.devcontainer/devcontainer.jsondiffbeforeafterboth
1{1{
2 "name": "Rust",2 "name": "Rust",
3 "dockerComposeFile": "./docker-compose.yml",3 "dockerComposeFile": "./docker-compose.yml",
4 "service": "nft_private",4 "service": "unique-chain",
5 "workspaceFolder": "/workspaces/nft_private",5 "workspaceFolder": "/workspaces/unique-chain",
6 "settings": { 6 "settings": {
7 "terminal.integrated.shell.linux": "/bin/bash",7 "terminal.integrated.shell.linux": "/bin/bash",
8 "lldb.executable": "/usr/bin/lldb",8 "lldb.executable": "/usr/bin/lldb",
modified.devcontainer/docker-compose.ymldiffbeforeafterboth
1version: '3'1version: '3'
2services:2services:
3 nft_private:3 unique-chain:
4 build: 4 build:
5 context: .5 context: .
6 environment:6 environment:
7 - JAEGER_AGENT_HOST=jaeger7 - JAEGER_AGENT_HOST=jaeger
8 - JAEGER_AGENT_PORT=68318 - JAEGER_AGENT_PORT=6831
9 volumes:9 volumes:
10 - ..:/workspaces/nft_private:cached10 - ..:/workspaces/unique-chain:cached
11 - ../../polkadot:/workspaces/polkadot:cached11 - ../../polkadot:/workspaces/polkadot:cached
12 - ../../polkadot-launch:/workspaces/polkadot-launch:cached12 - ../../polkadot-launch:/workspaces/polkadot-launch:cached
13 #- ../../frontier:/workspaces/frontier13 #- ../../frontier:/workspaces/frontier
modified.github/workflows/node_build_test.ymldiffbeforeafterboth
32 key: ${{ secrets.KEY }}32 key: ${{ secrets.KEY }}
33 port: ${{ secrets.SERVER_PORT }}33 port: ${{ secrets.SERVER_PORT }}
34 command_timeout: 300m34 command_timeout: 300m
35 script: |35 script: |
36 eval $(ssh-agent -s)36 eval $(ssh-agent -s)
37 ssh-add /home/devops/.ssh/git_hub37 ssh-add /home/devops/.ssh/git_hub
38 git clone git@github.com:UniqueNetwork/nft_private.git38 git clone git@github.com:UniqueNetwork/unique-chain.git
39 cd nft_private39 cd unique-chain
40 git checkout develop40 git checkout develop
41 # git pull --all41 # git pull --all
42 chmod +x ci_node.sh42 chmod +x ci_node.sh
43 ./ci_node.sh43 ./ci_node.sh
44 rm -rf /home/polkadot/nft_private44 rm -rf /home/polkadot/unique-chain
4545
modifiedCargo.lockdiffbeforeafterboth
5097 "frame-system-rpc-runtime-api",5097 "frame-system-rpc-runtime-api",
5098 "hex-literal",5098 "hex-literal",
5099 "nft-data-structs",5099 "nft-data-structs",
5100 "orml-vesting",
5100 "pallet-aura",5101 "pallet-aura",
5101 "pallet-balances",5102 "pallet-balances",
5102 "pallet-common",5103 "pallet-common",
5109 "pallet-fungible",5110 "pallet-fungible",
5110 "pallet-inflation",5111 "pallet-inflation",
5111 "pallet-nft",5112 "pallet-nft",
5112 "pallet-nft-transaction-payment",
5113 "pallet-nonfungible",5113 "pallet-nonfungible",
5114 "pallet-randomness-collective-flip",5114 "pallet-randomness-collective-flip",
5115 "pallet-refungible",5115 "pallet-refungible",
5120 "pallet-transaction-payment-rpc-runtime-api",5120 "pallet-transaction-payment-rpc-runtime-api",
5121 "pallet-treasury",5121 "pallet-treasury",
5122 "pallet-unq-scheduler",5122 "pallet-unq-scheduler",
5123 "pallet-vesting",
5124 "pallet-xcm",5123 "pallet-xcm",
5125 "parachain-info",5124 "parachain-info",
5126 "parity-scale-codec",5125 "parity-scale-codec",
5142 "sp-transaction-pool",5141 "sp-transaction-pool",
5143 "sp-version",5142 "sp-version",
5144 "substrate-wasm-builder",5143 "substrate-wasm-builder",
5144 "up-evm-mapping",
5145 "up-rpc",5145 "up-rpc",
5146 "xcm",5146 "xcm",
5147 "xcm-builder",5147 "xcm-builder",
5307 "num-traits",5307 "num-traits",
5308]5308]
5309
5310[[package]]
5311name = "orml-vesting"
5312version = "0.4.1-dev"
5313source = "git+https://github.com/UniqueNetwork/open-runtime-module-library#d69f226e332ae29b7b33d53d2f06f309d2986ea0"
5314dependencies = [
5315 "frame-support",
5316 "frame-system",
5317 "parity-scale-codec",
5318 "scale-info",
5319 "serde",
5320 "sp-io",
5321 "sp-runtime",
5322 "sp-std",
5323]
53095324
5310[[package]]5325[[package]]
5311name = "owning_ref"5326name = "owning_ref"
5575 "sp-core",5590 "sp-core",
5576 "sp-runtime",5591 "sp-runtime",
5577 "sp-std",5592 "sp-std",
5593 "up-evm-mapping",
5578]5594]
55795595
5580[[package]]5596[[package]]
5824 "sp-io",5840 "sp-io",
5825 "sp-runtime",5841 "sp-runtime",
5826 "sp-std",5842 "sp-std",
5843 "up-evm-mapping",
5827 "up-sponsorship",5844 "up-sponsorship",
5828]5845]
58295846
6072 "sp-io",6089 "sp-io",
6073 "sp-runtime",6090 "sp-runtime",
6074 "sp-std",6091 "sp-std",
6092 "up-evm-mapping",
6075 "up-sponsorship",6093 "up-sponsorship",
6076]6094]
6077
6078[[package]]
6079name = "pallet-nft-transaction-payment"
6080version = "3.0.0"
6081dependencies = [
6082 "frame-benchmarking",
6083 "frame-support",
6084 "frame-system",
6085 "pallet-transaction-payment",
6086 "parity-scale-codec",
6087 "scale-info",
6088 "serde",
6089 "sp-core",
6090 "sp-io",
6091 "sp-runtime",
6092 "sp-std",
6093 "up-sponsorship",
6094]
60956095
6096[[package]]6096[[package]]
6097name = "pallet-nicks"6097name = "pallet-nicks"
11831source = "registry+https://github.com/rust-lang/crates.io-index"11831source = "registry+https://github.com/rust-lang/crates.io-index"
11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
11833
11834[[package]]
11835name = "up-evm-mapping"
11836version = "0.1.0"
11837dependencies = [
11838 "frame-support",
11839 "sp-core",
11840]
1183311841
11834[[package]]11842[[package]]
11835name = "up-rpc"11843name = "up-rpc"
modifiedDockerfile-parachaindiffbeforeafterboth
98 npm install --global yarn && \98 npm install --global yarn && \
99 yarn99 yarn
100100
101COPY --from=builder /nft_parachain/target/$PROFILE/nft /nft_private/target/$PROFILE/101COPY --from=builder /nft_parachain/target/$PROFILE/nft /unique-chain/target/$PROFILE/
102COPY --from=builder-polkadot /nft_parachain/polkadot/target/$PROFILE/polkadot /polkadot/target/$PROFILE/102COPY --from=builder-polkadot /nft_parachain/polkadot/target/$PROFILE/polkadot /polkadot/target/$PROFILE/
103103
104CMD export NVM_DIR="$HOME/.nvm" && \104CMD export NVM_DIR="$HOME/.nvm" && \
modifiedclient/rpc/src/lib.rsdiffbeforeafterboth
3use codec::Decode;3use codec::Decode;
4use jsonrpc_core::{Error as RpcError, ErrorCode, Result};4use jsonrpc_core::{Error as RpcError, ErrorCode, Result};
5use jsonrpc_derive::rpc;5use jsonrpc_derive::rpc;
6use nft_data_structs::{CollectionId, TokenId};6use nft_data_structs::{Collection, CollectionId, CollectionStats, TokenId};
7use sp_api::{BlockId, BlockT, ProvideRuntimeApi};7use sp_api::{BlockId, BlockT, ProvideRuntimeApi};
8use sp_blockchain::HeaderBackend;8use sp_blockchain::HeaderBackend;
9use up_rpc::NftApi as NftRuntimeApi;9use up_rpc::NftApi as NftRuntimeApi;
86 collection: CollectionId,86 collection: CollectionId,
87 at: Option<BlockHash>,87 at: Option<BlockHash>,
88 ) -> Result<Vec<CrossAccountId>>;88 ) -> Result<Vec<CrossAccountId>>;
89 #[rpc(name = "nft_allowed")]
90 fn allowed(
91 &self,
92 collection: CollectionId,
93 user: CrossAccountId,
94 at: Option<BlockHash>,
95 ) -> Result<bool>;
89 #[rpc(name = "nft_lastTokenId")]96 #[rpc(name = "nft_lastTokenId")]
90 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;97 fn last_token_id(&self, collection: CollectionId, at: Option<BlockHash>) -> Result<TokenId>;
98 #[rpc(name = "nft_collectionById")]
99 fn collection_by_id(
100 &self,
101 collection: CollectionId,
102 at: Option<BlockHash>,
103 ) -> Result<Option<Collection<AccountId>>>;
104 #[rpc(name = "nft_collectionStats")]
105 fn collection_stats(&self, at: Option<BlockHash>) -> Result<CollectionStats>;
91}106}
92107
93pub struct Nft<C, P> {108pub struct Nft<C, P> {
160175
161 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>);176 pass_method!(adminlist(collection: CollectionId) -> Vec<CrossAccountId>);
162 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>);177 pass_method!(allowlist(collection: CollectionId) -> Vec<CrossAccountId>);
178 pass_method!(allowed(collection: CollectionId, user: CrossAccountId) -> bool);
163 pass_method!(last_token_id(collection: CollectionId) -> TokenId);179 pass_method!(last_token_id(collection: CollectionId) -> TokenId);
180 pass_method!(collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>>);
181 pass_method!(collection_stats() -> CollectionStats);
164}182}
165183
modifiedlaunch-config.jsondiffbeforeafterboth
55 },55 },
56 "parachains": [56 "parachains": [
57 {57 {
58 "bin": "../nft_private/target/release/nft",58 "bin": "../unique-chain/target/release/nft",
59 "id": "2000",59 "id": "2000",
60 "balance": "1000000000000000000000",60 "balance": "1000000000000000000000",
61 "nodes": [61 "nodes": [
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
15sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }15sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
16sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }16sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
17sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }17sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
18up-evm-mapping = { default-features = false, path = '../../primitives/evm-mapping' }
18nft-data-structs = { default-features = false, path = '../../primitives/nft' }19nft-data-structs = { default-features = false, path = '../../primitives/nft' }
19pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }20pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
20evm-coder = { default-features = false, path = '../../crates/evm-coder' }21evm-coder = { default-features = false, path = '../../crates/evm-coder' }
32 "frame-system/std",32 "frame-system/std",
33 "sp-runtime/std",33 "sp-runtime/std",
34 "sp-std/std",34 "sp-std/std",
35 "up-evm-mapping/std",
35 "nft-data-structs/std",36 "nft-data-structs/std",
36 "pallet-evm/std",37 "pallet-evm/std",
37]38]
modifiedpallets/common/src/account.rsdiffbeforeafterboth
2use codec::{Encode, EncodeLike, Decode};2use codec::{Encode, EncodeLike, Decode};
3use sp_core::H160;3use sp_core::H160;
4use scale_info::{Type, TypeInfo};4use scale_info::{Type, TypeInfo};
5use sp_core::crypto::AccountId32;
6use core::cmp::Ordering;5use core::cmp::Ordering;
7use serde::{Serialize, Deserialize};6use serde::{Serialize, Deserialize};
8use pallet_evm::AddressMapping;7use pallet_evm::AddressMapping;
9use sp_std::vec::Vec;8use sp_std::vec::Vec;
10use sp_std::clone::Clone;9use sp_std::clone::Clone;
10use up_evm_mapping::EvmBackwardsAddressMapping;
1111
12pub trait CrossAccountId<AccountId>:12pub trait CrossAccountId<AccountId>:
13 Encode + EncodeLike + Decode + TypeInfo + Clone + PartialEq + Ord + core::fmt::Debug + Default13 Encode + EncodeLike + Decode + TypeInfo + Clone + PartialEq + Ord + core::fmt::Debug + Default
177 }177 }
178}178}
179
180pub trait EvmBackwardsAddressMapping<AccountId> {
181 fn from_account_id(account_id: AccountId) -> H160;
182}
183
184/// Should have same mapping as EnsureAddressTruncated
185pub struct MapBackwardsAddressTruncated;
186impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
187 fn from_account_id(account_id: AccountId32) -> H160 {
188 let mut out = [0; 20];
189 out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
190 H160(out)
191 }
192}
193179
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
19pub fn create_collection_raw<T: Config, R>(19pub fn create_collection_raw<T: Config, R>(
20 owner: T::AccountId,20 owner: T::AccountId,
21 mode: CollectionMode,21 mode: CollectionMode,
22 handler: impl FnOnce(Collection<T>) -> Result<CollectionId, DispatchError>,22 handler: impl FnOnce(Collection<T::AccountId>) -> Result<CollectionId, DispatchError>,
23 cast: impl FnOnce(CollectionHandle<T>) -> R,23 cast: impl FnOnce(CollectionHandle<T>) -> R,
24) -> Result<R, DispatchError> {24) -> Result<R, DispatchError> {
25 T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());25 T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
12 COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,12 COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
13 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,13 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
14 COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,14 COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,
15 WithdrawReasons,15 WithdrawReasons, CollectionStats,
16};16};
17pub use pallet::*;17pub use pallet::*;
18use sp_core::H160;18use sp_core::H160;
26#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]26#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
27pub struct CollectionHandle<T: Config> {27pub struct CollectionHandle<T: Config> {
28 pub id: CollectionId,28 pub id: CollectionId,
29 collection: Collection<T>,29 collection: Collection<T::AccountId>,
30 pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,30 pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
31}31}
32impl<T: Config> CollectionHandle<T> {32impl<T: Config> CollectionHandle<T> {
78 }78 }
79}79}
80impl<T: Config> Deref for CollectionHandle<T> {80impl<T: Config> Deref for CollectionHandle<T> {
81 type Target = Collection<T>;81 type Target = Collection<T::AccountId>;
8282
83 fn deref(&self) -> &Self::Target {83 fn deref(&self) -> &Self::Target {
84 &self.collection84 &self.collection
141pub mod pallet {141pub mod pallet {
142 use super::*;142 use super::*;
143 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};143 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};
144 use account::{EvmBackwardsAddressMapping, CrossAccountId};144 use account::CrossAccountId;
145 use frame_support::traits::Currency;145 use frame_support::traits::Currency;
146 use nft_data_structs::TokenId;146 use nft_data_structs::TokenId;
147 use scale_info::TypeInfo;147 use scale_info::TypeInfo;
153 type CrossAccountId: CrossAccountId<Self::AccountId>;153 type CrossAccountId: CrossAccountId<Self::AccountId>;
154154
155 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;155 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
156 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;156 type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;
157157
158 type Currency: Currency<Self::AccountId>;158 type Currency: Currency<Self::AccountId>;
159 type CollectionCreationPrice: Get<159 type CollectionCreationPrice: Get<
311 pub type CollectionById<T> = StorageMap<311 pub type CollectionById<T> = StorageMap<
312 Hasher = Blake2_128Concat,312 Hasher = Blake2_128Concat,
313 Key = CollectionId,313 Key = CollectionId,
314 Value = Collection<T>,314 Value = Collection<<T as frame_system::Config>::AccountId>,
315 QueryKind = OptionQuery,315 QueryKind = OptionQuery,
316 >;316 >;
317317
345 QueryKind = ValueQuery,345 QueryKind = ValueQuery,
346 >;346 >;
347
348 /// Not used by code, exists only to provide some types to metadata
349 #[pallet::storage]
350 pub type DummyStorageValue<T> =
351 StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;
347}352}
348353
349impl<T: Config> Pallet<T> {354impl<T: Config> Pallet<T> {
355 );360 );
356 Ok(())361 Ok(())
357 }362 }
363 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
364 <IsAdmin<T>>::iter_prefix((collection,))
365 .map(|(a, _)| a)
366 .collect()
367 }
368 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
369 <Allowlist<T>>::iter_prefix((collection,))
370 .map(|(a, _)| a)
371 .collect()
372 }
373 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {
374 <Allowlist<T>>::get((collection, user))
375 }
376 pub fn collection_stats() -> CollectionStats {
377 let created = <CreatedCollectionCount<T>>::get();
378 let destroyed = <DestroyedCollectionCount<T>>::get();
379 CollectionStats {
380 created: created.0,
381 destroyed: destroyed.0,
382 alive: created.0 - destroyed.0,
383 }
384 }
358}385}
359386
360impl<T: Config> Pallet<T> {387impl<T: Config> Pallet<T> {
361 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {388 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
362 {389 {
363 ensure!(390 ensure!(
364 data.name.len() <= MAX_COLLECTION_NAME_LENGTH,391 data.name.len() <= MAX_COLLECTION_NAME_LENGTH,
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
5959
60 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {60 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
61 self.0.consume_sload()?;61 self.0.consume_sload()?;
62 Ok(<Pallet<T>>::allowed(contract_address, user, true))62 Ok(<Pallet<T>>::allowed(contract_address, user)
63 || !<AllowlistEnabled<T>>::get(contract_address))
63 }64 }
6465
65 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {66 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
113 value: sp_core::U256,114 value: sp_core::U256,
114 ) -> Option<PrecompileOutput> {115 ) -> Option<PrecompileOutput> {
115 // TODO: Extract to another OnMethodCall handler116 // TODO: Extract to another OnMethodCall handler
116 if !<Pallet<T>>::allowed(*target, *source, true) {117 if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {
117 return Some(PrecompileOutput {118 return Some(PrecompileOutput {
118 exit_status: ExitReason::Revert(ExitRevert::Reverted),119 exit_status: ExitReason::Revert(ExitRevert::Reverted),
119 cost: 0,120 cost: 0,
151pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);152pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
152impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {153impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
153 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {154 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
154 if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {155 if !<SelfSponsoring<T>>::get(&call.0) {
156 return None;
157 }
158 if !<Pallet<T>>::allowed(call.0, *who) {
159 return None;
160 }
155 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;161 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
162
156 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {163 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
157 let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);164 let limit = <SponsoringRateLimit<T>>::get(&call.0);
165
158 let limit_time = last_tx_block + rate_limit;166 let timeout = last_tx_block + limit.into();
159167 if block_number < timeout {
160 if block_number > limit_time {168 return None;
161 <SponsorBasket<T>>::insert(&call.0, who, block_number);169 }
162 return Some(call.0);
163 }
164 } else {170 }
171
165 <SponsorBasket<T>>::insert(&call.0, who, block_number);172 <SponsorBasket<T>>::insert(&call.0, who, block_number);
173
166 return Some(call.0);174 Some(call.0)
167 }
168 }
169 None
170 }175 }
171}176}
172177
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);
77 }77 }
7878
79 /// Default is returned if allowlist is disabled
80 pub fn allowed(contract: H160, user: H160, default: bool) -> bool {79 pub fn allowed(contract: H160, user: H160) -> bool {
81 if !<AllowlistEnabled<T>>::get(contract) {
82 return default;
83 }
84 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user80 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
85 }81 }
8682
modifiedpallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth
15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
18up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
1819
19[dependencies.codec]20[dependencies.codec]
20default-features = false21default-features = false
35 "pallet-ethereum/std",36 "pallet-ethereum/std",
36 "fp-evm/std",37 "fp-evm/std",
37 "up-sponsorship/std",38 "up-sponsorship/std",
39 "up-evm-mapping/std",
38]40]
3941
modifiedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]
22
3pub use pallet::*;
4
5#[frame_support::pallet]
6pub mod pallet {
7 use core::marker::PhantomData;3use core::marker::PhantomData;
4use fp_evm::WithdrawReason;
8 use frame_support::traits::Currency;5use frame_support::traits::{Currency, IsSubType};
9 use pallet_evm::EVMCurrencyAdapter;6pub use pallet::*;
10 use fp_evm::WithdrawReason;7use pallet_evm::{EVMCurrencyAdapter, EnsureAddressOrigin};
11 use sp_core::{H160, U256};8use sp_core::{H160, U256};
12 use sp_runtime::TransactionOutcome;9use sp_runtime::TransactionOutcome;
13 use up_sponsorship::SponsorshipHandler;10use up_sponsorship::SponsorshipHandler;
11use up_evm_mapping::EvmBackwardsAddressMapping;
12use pallet_evm::AddressMapping;
13
14#[frame_support::pallet]
15pub mod pallet {
16 use super::*;
17
14 use sp_std::vec::Vec;18 use frame_support::traits::Currency;
15
16 type NegativeImbalanceOf<C, T> =
17 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;19 use sp_std::vec::Vec;
1820
19 #[pallet::config]21 #[pallet::config]
20 pub trait Config: frame_system::Config {22 pub trait Config: frame_system::Config {
21 type SponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;23 type EvmSponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
22 type Currency: Currency<Self::AccountId>;24 type Currency: Currency<Self::AccountId>;
25 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
26 type EvmAddressMapping: AddressMapping<Self::AccountId>;
23 }27 }
2428
25 #[pallet::pallet]29 #[pallet::pallet]
26 #[pallet::generate_store(pub(super) trait Store)]30 #[pallet::generate_store(pub(super) trait Store)]
27 pub struct Pallet<T>(_);31 pub struct Pallet<T>(_);
32}
33
34type NegativeImbalanceOf<C, T> =
35 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
2836
29 pub struct ChargeEvmLiquidityInfo<T>37pub struct ChargeEvmLiquidityInfo<T>
30 where38where
42 WithdrawReason::Call { target, input } => {50 WithdrawReason::Call { target, input } => {
43 // This method is only used for checking, we shouldn't touch storage in it51 // This method is only used for checking, we shouldn't touch storage in it
44 frame_support::storage::with_transaction(|| {52 frame_support::storage::with_transaction(|| {
45 TransactionOutcome::Rollback(T::SponsorshipHandler::get_sponsor(53 TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
46 &origin,54 &origin,
47 &(*target, input.clone()),55 &(*target, input.clone()),
48 ))56 ))
68 ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {75 ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
69 let mut who_pays_fee = *who;76 let mut who_pays_fee = *who;
70 if let WithdrawReason::Call { target, input } = &reason {77 if let WithdrawReason::Call { target, input } = &reason {
71 who_pays_fee = T::SponsorshipHandler::get_sponsor(who, &(*target, input.clone()))78 who_pays_fee = T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
72 .unwrap_or(who_pays_fee);79 .unwrap_or(who_pays_fee);
73 }80 }
74 let negative_imbalance =81 let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
95 )101 )
96 }102 }
97 }103}
98}104
105/// Implements sponsoring for evm calls performed from pallet-evm (via api.tx.ethereum.transact/api.tx.evm.call)
106pub struct BridgeSponsorshipHandler<T>(PhantomData<T>);
107impl<T, C> SponsorshipHandler<T::AccountId, C> for BridgeSponsorshipHandler<T>
108where
109 T: Config + pallet_evm::Config,
110 C: IsSubType<pallet_evm::Call<T>>,
111{
112 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
113 match call.is_sub_type()? {
114 pallet_evm::Call::call {
115 source,
116 target,
117 input,
118 ..
119 } => {
120 let _ = T::CallOrigin::ensure_address_origin(
121 source,
122 <frame_system::RawOrigin<T::AccountId>>::Signed(who.clone()).into(),
123 )
124 .ok()?;
125 let who = T::EvmBackwardsAddressMapping::from_account_id(who.clone());
126 // Effects from EvmSponsorshipHandler are applied in OnChargeEvmTransaction by pallet_evm::runner
127 // TODO: Should we implement simulation mode (test, but do not apply effects) in `up-sponsorship`?
128 let sponsor = frame_support::storage::with_transaction(|| {
129 TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
130 &who,
131 &(target.clone(), input.clone()),
132 ))
133 })?;
134 let sponsor = T::EvmAddressMapping::into_account_id(sponsor);
135 Some(sponsor)
136 }
137 _ => None,
138 }
139 }
140}
99141
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
52 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;52 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;
5353
54 #[pallet::storage]54 #[pallet::storage]
55 pub(super) type Balance<T: Config> = StorageNMap<55 pub type Balance<T: Config> = StorageNMap<
56 Key = (56 Key = (
57 Key<Twox64Concat, CollectionId>,57 Key<Twox64Concat, CollectionId>,
58 Key<Blake2_128Concat, T::CrossAccountId>,58 Key<Blake2_128Concat, T::CrossAccountId>,
91}91}
9292
93impl<T: Config> Pallet<T> {93impl<T: Config> Pallet<T> {
94 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {94 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
95 PalletCommon::init_collection(data)95 <PalletCommon<T>>::init_collection(data)
96 }96 }
97 pub fn destroy_collection(97 pub fn destroy_collection(
98 collection: FungibleHandle<T>,98 collection: FungibleHandle<T>,
deletedpallets/nft-transaction-payment/Cargo.tomldiffbeforeafterboth

no changes

deletedpallets/nft-transaction-payment/README.mddiffbeforeafterboth

no changes

deletedpallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth

no changes

modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
34 'fp-evm/std',34 'fp-evm/std',
35 'nft-data-structs/std',35 'nft-data-structs/std',
36 'up-sponsorship/std',36 'up-sponsorship/std',
37 'up-evm-mapping/std',
37 'sp-std/std',38 'sp-std/std',
38 'sp-api/std',39 'sp-api/std',
39 'sp-runtime/std',40 'sp-runtime/std',
135sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.12" }136sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.12" }
136137
137up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 138up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
138139up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
139evm-coder = { default-features = false, path = "../../crates/evm-coder" }140evm-coder = { default-features = false, path = "../../crates/evm-coder" }
140pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }141pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
141primitive-types = { version = "0.10.1", default-features = false, features = [142primitive-types = { version = "0.10.1", default-features = false, features = [
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction1//! Implements EVM sponsoring logic via OnChargeEVMTransaction
22
3use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};3use crate::{Config, sponsorship::*};
4use evm_coder::{Call, abi::AbiReader};4use evm_coder::{Call, abi::AbiReader};
5use frame_support::{
6 storage::{StorageDoubleMap},
7};
8use pallet_common::eth::map_eth_to_id;5use pallet_common::{CollectionHandle, eth::map_eth_to_id};
9use sp_core::H160;6use sp_core::H160;
10use sp_std::prelude::*;7use sp_std::prelude::*;
11use up_sponsorship::SponsorshipHandler;8use up_sponsorship::SponsorshipHandler;
12use core::marker::PhantomData;9use core::marker::PhantomData;
13use core::convert::TryInto;10use core::convert::TryInto;
14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};11use nft_data_structs::TokenId;
12use up_evm_mapping::EvmBackwardsAddressMapping;
15use pallet_common::{13use pallet_common::account::CrossAccountId;
16 CollectionById,
17 account::{CrossAccountId, EvmBackwardsAddressMapping},
18};
1914
20use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};15use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};16use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
22
23struct AnyError;
24
25fn try_sponsor<T: Config>(
26 caller: &H160,
27 collection_id: CollectionId,
28 collection: &Collection<T>,
29 call: &[u8],
30) -> Result<(), AnyError> {
31 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
32 match &collection.mode {
33 crate::CollectionMode::NFT => {
34 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)
35 .map_err(|_| AnyError)?
36 .ok_or(AnyError)?;
37 match call {
38 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {
39 token_id,
40 ..
41 })
42 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
43 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
44 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
45 let collection_limits = &collection.limits;
46 let limit =
47 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
48
49 let mut sponsor = true;
50 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
51 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);
52 let limit_time = last_tx_block + limit.into();
53 if block_number <= limit_time {
54 sponsor = false;
55 }
56 }
57 if sponsor {
58 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);
59 return Ok(());
60 }
61 }
62 _ => {}
63 }
64 }
65 crate::CollectionMode::Fungible(_) => {
66 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)
67 .map_err(|_| AnyError)?
68 .ok_or(AnyError)?;
69 #[allow(clippy::single_match)]
70 match call {
71 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
72 let who = T::CrossAccountId::from_eth(*caller);
73 let collection_limits = &collection.limits;
74 let limit = collection_limits
75 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
76
77 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
78 let mut sponsored = true;
79 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {
80 let last_tx_block =
81 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());
82 let limit_time = last_tx_block + limit.into();
83 if block_number <= limit_time {
84 sponsored = false;
85 }
86 }
87 if sponsored {
88 <FungibleTransferBasket<T>>::insert(
89 collection_id,
90 who.as_sub(),
91 block_number,
92 );
93 return Ok(());
94 }
95 }
96 _ => {}
97 }
98 }
99 _ => {}
100 }
101 Err(AnyError)
102}
10317
104pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);18pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
105impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {19impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
106 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {20 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
107 if let Some(collection_id) = map_eth_to_id(&call.0) {21 let collection_id = map_eth_to_id(&call.0)?;
108 if let Some(collection) = <CollectionById<T>>::get(collection_id) {22 let collection = <CollectionHandle<T>>::new(collection_id)?;
109 if !collection.sponsorship.confirmed() {23 let sponsor = collection.sponsorship.sponsor()?.clone();
110 return None;24 let sponsor =
111 }25 <T as pallet_common::Config>::EvmBackwardsAddressMapping::from_account_id(sponsor);
26 let who = T::CrossAccountId::from_eth(*who);
27 let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
28 match &collection.mode {
29 crate::CollectionMode::NFT => {
30 let call = UniqueNFTCall::parse(method_id, &mut reader).ok()??;
31 match call {
32 UniqueNFTCall::ERC721UniqueExtensions(
33 ERC721UniqueExtensionsCall::Transfer { token_id, .. },
34 ) => {
35 let token_id: TokenId = token_id.try_into().ok()?;
112 if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {36 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
113 return collection37 }
114 .sponsorship38 UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {
115 .sponsor()39 let token_id: TokenId = token_id.try_into().ok()?;
116 .cloned()
117 .map(T::EvmBackwardsAddressMapping::from_account_id);40 let from = T::CrossAccountId::from_eth(from);
118 }41 withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)
119 }42 }
120 }43 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
44 let token_id: TokenId = token_id.try_into().ok()?;
45 withdraw_approve::<T>(&collection, who.as_sub(), &token_id)
46 .map(|()| sponsor)
47 }
48 _ => None,
49 }
50 }
51 crate::CollectionMode::Fungible(_) => {
52 let call = UniqueFungibleCall::parse(method_id, &mut reader).ok()??;
53 #[allow(clippy::single_match)]
54 match call {
55 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
56 withdraw_transfer::<T>(&collection, &who, &TokenId::default())
57 .map(|()| sponsor)
58 }
59 UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {
60 let from = T::CrossAccountId::from_eth(from);
61 withdraw_transfer::<T>(&collection, &from, &TokenId::default())
62 .map(|()| sponsor)
63 }
64 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
65 withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())
66 .map(|()| sponsor)
67 }
68 _ => None,
69 }
70 }
121 None71 _ => None,
72 }
122 }73 }
123}74}
12475
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
42 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,42 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
43};43};
44use pallet_common::{44use pallet_common::{
45 account::CrossAccountId, CollectionHandle, IsAdmin, Pallet as PalletCommon,45 account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
46 Error as CommonError, CommonWeightInfo, Allowlist,46 CommonWeightInfo,
47};47};
48use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};48use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};
49use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};49use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};
136 //#region Tokens transfer rate limit baskets136 //#region Tokens transfer rate limit baskets
137 /// (Collection id (controlled?2), who created (real))137 /// (Collection id (controlled?2), who created (real))
138 /// TODO: Off chain worker should remove from this map when collection gets removed138 /// TODO: Off chain worker should remove from this map when collection gets removed
139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
140 /// Collection id (controlled?2), token id (controlled?2)140 /// Collection id (controlled?2), token id (controlled?2)
141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
142 /// Collection id (controlled?2), owning user (real)142 /// Collection id (controlled?2), owning user (real)
143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
144 /// Collection id (controlled?2), token id (controlled?2)144 /// Collection id (controlled?2), token id (controlled?2)
145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
146 //#endregion146 //#endregion
147147
148 /// Variable metadata sponsoring148 /// Variable metadata sponsoring
149 /// Collection id (controlled?2), token id (controlled?2)149 /// Collection id (controlled?2), token id (controlled?2)
150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
151 /// Approval sponsoring
152 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
153 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
154 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
151 }155 }
152}156}
153157
190 let who = ensure_signed(origin)?;194 let who = ensure_signed(origin)?;
191195
192 // Create new collection196 // Create new collection
193 let new_collection = Collection::<T> {197 let new_collection = Collection {
194 owner: who.clone(),198 owner: who.clone(),
195 name: collection_name,199 name: collection_name,
196 mode: mode.clone(),200 mode: mode.clone(),
208 };212 };
209213
210 let _id = match mode {214 let _id = match mode {
211 CollectionMode::NFT => {PalletNonfungible::init_collection(new_collection)?},215 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},
212 CollectionMode::Fungible(decimal_points) => {216 CollectionMode::Fungible(decimal_points) => {
213 // check params217 // check params
214 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);218 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
215 PalletFungible::init_collection(new_collection)?219 <PalletFungible<T>>::init_collection(new_collection)?
216 }220 }
217 CollectionMode::ReFungible => {221 CollectionMode::ReFungible => {
218 PalletRefungible::init_collection(new_collection)?222 <PalletRefungible<T>>::init_collection(new_collection)?
219 }223 }
220 };224 };
221225
249253
250 <NftTransferBasket<T>>::remove_prefix(collection_id, None);254 <NftTransferBasket<T>>::remove_prefix(collection_id, None);
251 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);255 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
252 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);256 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);
253257
254 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);258 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
259 <NftApproveBasket<T>>::remove_prefix(collection_id, None);
260 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);
261 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);
255262
256 Ok(())263 Ok(())
257 }264 }
592 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {599 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
593 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
594601
595 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))602 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;
603 if value == 1 {
604 <NftTransferBasket<T>>::remove(collection_id, item_id);
605 <NftApproveBasket<T>>::remove(collection_id, item_id);
606 }
607 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?
608 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());
609 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));
610 Ok(post_info)
596 }611 }
597612
598 /// Destroys a concrete instance of NFT on behalf of the owner613 /// Destroys a concrete instance of NFT on behalf of the owner
939 }954 }
940}955}
941
942// TODO: limit returned entries?
943impl<T: Config> Pallet<T> {
944 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
945 <IsAdmin<T>>::iter_prefix((collection,))
946 .map(|(a, _)| a)
947 .collect()
948 }
949 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {
950 <Allowlist<T>>::iter_prefix((collection,))
951 .map(|(a, _)| a)
952 .collect()
953 }
954}
955956
modifiedpallets/nft/src/sponsorship.rsdiffbeforeafterboth
1use crate::{1use crate::{
2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode,3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
4 FungibleApproveBasket, RefungibleApproveBasket,
4};5};
5use core::marker::PhantomData;6use core::marker::PhantomData;
6use up_sponsorship::SponsorshipHandler;7use up_sponsorship::SponsorshipHandler;
7use frame_support::{8use frame_support::{
8 traits::{IsSubType},9 traits::{IsSubType},
9 storage::{StorageMap, StorageDoubleMap},10 storage::{StorageMap, StorageDoubleMap, StorageNMap},
10};11};
11use nft_data_structs::{12use nft_data_structs::{
12 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,13 CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MetaUpdatePermission,
13 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,
14};15};
15use pallet_common::{CollectionById};16use pallet_common::{CollectionHandle};
17use pallet_common::account::CrossAccountId;
18
19pub fn withdraw_transfer<T: Config>(
20 collection: &CollectionHandle<T>,
21 who: &T::CrossAccountId,
22 item_id: &TokenId,
23) -> Option<()> {
24 // preliminary sponsoring correctness check
25 match collection.mode {
26 CollectionMode::NFT => {
27 let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
28 if !owner.conv_eq(who) {
29 return None;
30 }
31 }
32 CollectionMode::Fungible(_) => {
33 if item_id != &TokenId::default() {
34 return None;
35 }
36 if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
37 return None;
38 }
39 }
40 CollectionMode::ReFungible => {
41 if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
42 return None;
43 }
44 }
45 }
46
47 // sponsor timeout
48 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
49 let limit = collection
50 .limits
51 .sponsor_transfer_timeout(match collection.mode {
52 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
53 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
54 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
55 });
56
57 let last_tx_block = match collection.mode {
58 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
59 CollectionMode::Fungible(_) => {
60 <FungibleTransferBasket<T>>::get(collection.id, who.as_sub())
61 }
62 CollectionMode::ReFungible => {
63 <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who.as_sub()))
64 }
65 };
66
67 if let Some(last_tx_block) = last_tx_block {
68 let timeout = last_tx_block + limit.into();
69 if block_number < timeout {
70 return None;
71 }
72 }
73
74 match collection.mode {
75 CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
76 CollectionMode::Fungible(_) => {
77 <FungibleTransferBasket<T>>::insert(collection.id, who.as_sub(), block_number)
78 }
79 CollectionMode::ReFungible => <ReFungibleTransferBasket<T>>::insert(
80 (collection.id, item_id, who.as_sub()),
81 block_number,
82 ),
83 };
84
85 Some(())
86}
87
88pub fn withdraw_create_item<T: Config>(
89 collection: &CollectionHandle<T>,
90 who: &T::AccountId,
91 _properties: &CreateItemData,
92) -> Option<()> {
93 if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
94 return None;
95 }
96
97 // sponsor timeout
98 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
99 let limit = collection
100 .limits
101 .sponsor_transfer_timeout(match _properties {
102 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
103 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
104 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
105 });
106
107 if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {
108 let timeout = last_tx_block + limit.into();
109 if block_number < timeout {
110 return None;
111 }
112 }
113
114 CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);
115
116 Some(())
117}
118
119pub fn withdraw_set_variable_meta_data<T: Config>(
120 who: &T::CrossAccountId,
121 collection: &CollectionHandle<T>,
122 item_id: &TokenId,
123 data: &[u8],
124) -> Option<()> {
125 // TODO: make it work for admins
126 if collection.meta_update_permission != MetaUpdatePermission::ItemOwner {
127 return None;
128 }
129 // preliminary sponsoring correctness check
130 match collection.mode {
131 CollectionMode::NFT => {
132 let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
133 if !owner.conv_eq(who) {
134 return None;
135 }
136 }
137 CollectionMode::Fungible(_) => {
138 if item_id != &TokenId::default() {
139 return None;
140 }
141 if <pallet_fungible::Balance<T>>::get((collection.id, who)) == 0 {
142 return None;
143 }
144 }
145 CollectionMode::ReFungible => {
146 if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
147 return None;
148 }
149 }
150 }
151
152 // Can't sponsor fungible collection, this tx will be rejected
153 // as invalid
154 if matches!(collection.mode, CollectionMode::Fungible(_)) {
155 return None;
156 }
157 if data.len() > collection.limits.sponsored_data_size() as usize {
158 return None;
159 }
160
161 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
162 let limit = collection.limits.sponsored_data_rate_limit()?;
163
164 if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
165 let timeout = last_tx_block + limit.into();
166 if block_number < timeout {
167 return None;
168 }
169 }
170
171 <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
172
173 Some(())
174}
175
176pub fn withdraw_approve<T: Config>(
177 collection: &CollectionHandle<T>,
178 who: &T::AccountId,
179 item_id: &TokenId,
180) -> Option<()> {
181 // sponsor timeout
182 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
183 let limit = collection.limits.sponsor_approve_timeout();
184
185 let last_tx_block = match collection.mode {
186 CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
187 CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
188 CollectionMode::ReFungible => {
189 <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
190 }
191 };
192
193 if let Some(last_tx_block) = last_tx_block {
194 let timeout = last_tx_block + limit.into();
195 if block_number < timeout {
196 return None;
197 }
198 }
199
200 match collection.mode {
201 CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
202 CollectionMode::Fungible(_) => {
203 <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
204 }
205 CollectionMode::ReFungible => {
206 <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
207 }
208 };
209
210 Some(())
211}
212
213fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
214 let collection = CollectionHandle::new(id)?;
215 let sponsor = collection.sponsorship.sponsor().cloned()?;
216 Some((sponsor, collection))
217}
16218
17pub struct NftSponsorshipHandler<T>(PhantomData<T>);219pub struct NftSponsorshipHandler<T>(PhantomData<T>);
18impl<T: Config> NftSponsorshipHandler<T> {
19 pub fn withdraw_create_item(
20 who: &T::AccountId,
21 collection_id: &CollectionId,
22 _properties: &CreateItemData,
23 ) -> Option<T::AccountId> {
24 let collection = CollectionById::<T>::get(collection_id)?;
25
26 // sponsor timeout
27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
28
29 let limit = collection
30 .limits
31 .sponsor_transfer_timeout(match _properties {
32 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
33 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
34 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
35 });
36 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
37 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
38 let limit_time = last_tx_block + limit.into();
39 if block_number <= limit_time {
40 return None;
41 }
42 }
43 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
44
45 // check free create limit
46 if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
47 collection.sponsorship.sponsor().cloned()
48 } else {
49 None
50 }
51 }
52
53 pub fn withdraw_transfer(
54 who: &T::AccountId,
55 collection_id: &CollectionId,
56 item_id: &TokenId,
57 ) -> Option<T::AccountId> {
58 let collection = CollectionById::<T>::get(collection_id)?;
59
60 let mut sponsor_transfer = false;
61 if collection.sponsorship.confirmed() {
62 let collection_limits = collection.limits.clone();
63 let collection_mode = collection.mode.clone();
64
65 // sponsor timeout
66 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
67 sponsor_transfer = match collection_mode {
68 CollectionMode::NFT => {
69 // get correct limit
70 let limit =
71 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
72
73 let mut sponsored = true;
74 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
75 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);
76 let limit_time = last_tx_block + limit.into();
77 if block_number <= limit_time {
78 sponsored = false;
79 }
80 }
81 if sponsored {
82 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
83 }
84
85 sponsored
86 }
87 CollectionMode::Fungible(_) => {
88 // get correct limit
89 let limit = collection_limits
90 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
91
92 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
93 let mut sponsored = true;
94 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {
95 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);
96 let limit_time = last_tx_block + limit.into();
97 if block_number <= limit_time {
98 sponsored = false;
99 }
100 }
101 if sponsored {
102 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
103 }
104
105 sponsored
106 }
107 CollectionMode::ReFungible => {
108 // get correct limit
109 let limit = collection_limits
110 .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
111
112 let mut sponsored = true;
113 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
114 let last_tx_block =
115 ReFungibleTransferBasket::<T>::get(collection_id, item_id);
116 let limit_time = last_tx_block + limit.into();
117 if block_number <= limit_time {
118 sponsored = false;
119 }
120 }
121 if sponsored {
122 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
123 }
124
125 sponsored
126 }
127 };
128 }
129
130 if !sponsor_transfer {
131 None
132 } else {
133 collection.sponsorship.sponsor().cloned()
134 }
135 }
136
137 pub fn withdraw_set_variable_meta_data(
138 collection_id: &CollectionId,
139 item_id: &TokenId,
140 data: &[u8],
141 ) -> Option<T::AccountId> {
142 let mut sponsor_metadata_changes = false;
143
144 let collection = CollectionById::<T>::get(collection_id)?;
145
146 if collection.sponsorship.confirmed() &&
147 // Can't sponsor fungible collection, this tx will be rejected
148 // as invalid
149 !matches!(collection.mode, CollectionMode::Fungible(_)) &&
150 data.len() <= collection.limits.sponsored_data_size() as usize
151 {
152 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
153 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
154
155 if VariableMetaDataBasket::<T>::get(collection_id, item_id)
156 .map(|last_block| block_number - last_block > rate_limit.into())
157 .unwrap_or(true)
158 {
159 sponsor_metadata_changes = true;
160 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
161 }
162 }
163 }
164
165 if !sponsor_metadata_changes {
166 None
167 } else {
168 collection.sponsorship.sponsor().cloned()
169 }
170 }
171}
172
173impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>220impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
174where221where
181 collection_id,228 collection_id,
182 data,229 data,
183 ..230 ..
184 } => Self::withdraw_create_item(who, collection_id, data),231 } => {
232 let (sponsor, collection) = load(*collection_id)?;
233 withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)
234 }
185 Call::transfer {235 Call::transfer {
186 collection_id,236 collection_id,
187 item_id,237 item_id,
188 ..238 ..
189 } => Self::withdraw_transfer(who, collection_id, item_id),239 } => {
240 let (sponsor, collection) = load(*collection_id)?;
241 withdraw_transfer::<T>(
242 &collection,
243 &T::CrossAccountId::from_sub(who.clone()),
244 item_id,
245 )
246 .map(|()| sponsor)
247 }
248 Call::transfer_from {
249 collection_id,
250 item_id,
251 from,
252 ..
253 } => {
254 let (sponsor, collection) = load(*collection_id)?;
255 withdraw_transfer::<T>(&collection, from, item_id).map(|()| sponsor)
256 }
257 Call::approve {
258 collection_id,
259 item_id,
260 ..
261 } => {
262 let (sponsor, collection) = load(*collection_id)?;
263 withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
264 }
190 Call::set_variable_meta_data {265 Call::set_variable_meta_data {
191 collection_id,266 collection_id,
192 item_id,267 item_id,
193 data,268 data,
194 } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),269 } => {
270 let (sponsor, collection) = load(*collection_id)?;
271 withdraw_set_variable_meta_data::<T>(
272 &T::CrossAccountId::from_sub(who.clone()),
273 &collection,
274 item_id,
275 data,
276 )
277 .map(|()| sponsor)
278 }
195 _ => None,279 _ => None,
196 }280 }
197 }281 }
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
58 }58 }
5959
60 #[pallet::pallet]60 #[pallet::pallet]
61 #[pallet::generate_store(pub(super) trait Store)]61 #[pallet::generate_store(pub trait Store)]
62 pub struct Pallet<T>(_);62 pub struct Pallet<T>(_);
6363
64 #[pallet::storage]64 #[pallet::storage]
65 pub(super) type TokensMinted<T: Config> =65 pub type TokensMinted<T: Config> =
66 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;66 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
67 #[pallet::storage]67 #[pallet::storage]
68 pub(super) type TokensBurnt<T: Config> =68 pub type TokensBurnt<T: Config> =
69 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;69 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
7070
71 #[pallet::storage]71 #[pallet::storage]
72 pub(super) type TokenData<T: Config> = StorageNMap<72 pub type TokenData<T: Config> = StorageNMap<
73 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),73 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
74 Value = ItemData<T>,74 Value = ItemData<T>,
75 QueryKind = OptionQuery,75 QueryKind = OptionQuery,
76 >;76 >;
7777
78 /// Used to enumerate tokens owned by account78 /// Used to enumerate tokens owned by account
79 #[pallet::storage]79 #[pallet::storage]
80 pub(super) type Owned<T: Config> = StorageNMap<80 pub type Owned<T: Config> = StorageNMap<
81 Key = (81 Key = (
82 Key<Twox64Concat, CollectionId>,82 Key<Twox64Concat, CollectionId>,
83 Key<Blake2_128Concat, T::CrossAccountId>,83 Key<Blake2_128Concat, T::CrossAccountId>,
88 >;88 >;
8989
90 #[pallet::storage]90 #[pallet::storage]
91 pub(super) type AccountBalance<T: Config> = StorageNMap<91 pub type AccountBalance<T: Config> = StorageNMap<
92 Key = (92 Key = (
93 Key<Twox64Concat, CollectionId>,93 Key<Twox64Concat, CollectionId>,
94 Key<Blake2_128Concat, T::CrossAccountId>,94 Key<Blake2_128Concat, T::CrossAccountId>,
98 >;98 >;
9999
100 #[pallet::storage]100 #[pallet::storage]
101 pub(super) type Allowance<T: Config> = StorageNMap<101 pub type Allowance<T: Config> = StorageNMap<
102 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),102 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
103 Value = T::CrossAccountId,103 Value = T::CrossAccountId,
104 QueryKind = OptionQuery,104 QueryKind = OptionQuery,
133133
134// unchecked calls skips any permission checks134// unchecked calls skips any permission checks
135impl<T: Config> Pallet<T> {135impl<T: Config> Pallet<T> {
136 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {136 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
137 PalletCommon::init_collection(data)137 <PalletCommon<T>>::init_collection(data)
138 }138 }
139 pub fn destroy_collection(139 pub fn destroy_collection(
140 collection: NonfungibleHandle<T>,140 collection: NonfungibleHandle<T>,
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
54 }54 }
5555
56 #[pallet::pallet]56 #[pallet::pallet]
57 #[pallet::generate_store(pub(super) trait Store)]57 #[pallet::generate_store(pub trait Store)]
58 pub struct Pallet<T>(_);58 pub struct Pallet<T>(_);
5959
60 #[pallet::storage]60 #[pallet::storage]
61 pub(super) type TokensMinted<T: Config> =61 pub type TokensMinted<T: Config> =
62 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;62 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
63 #[pallet::storage]63 #[pallet::storage]
64 pub(super) type TokensBurnt<T: Config> =64 pub type TokensBurnt<T: Config> =
65 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;65 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
6666
67 #[pallet::storage]67 #[pallet::storage]
68 pub(super) type TokenData<T: Config> = StorageNMap<68 pub type TokenData<T: Config> = StorageNMap<
69 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),69 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
70 Value = ItemData,70 Value = ItemData,
71 QueryKind = ValueQuery,71 QueryKind = ValueQuery,
72 >;72 >;
7373
74 #[pallet::storage]74 #[pallet::storage]
75 pub(super) type TotalSupply<T: Config> = StorageNMap<75 pub type TotalSupply<T: Config> = StorageNMap<
76 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),76 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
77 Value = u128,77 Value = u128,
78 QueryKind = ValueQuery,78 QueryKind = ValueQuery,
79 >;79 >;
8080
81 /// Used to enumerate tokens owned by account81 /// Used to enumerate tokens owned by account
82 #[pallet::storage]82 #[pallet::storage]
83 pub(super) type Owned<T: Config> = StorageNMap<83 pub type Owned<T: Config> = StorageNMap<
84 Key = (84 Key = (
85 Key<Twox64Concat, CollectionId>,85 Key<Twox64Concat, CollectionId>,
86 Key<Blake2_128Concat, T::CrossAccountId>,86 Key<Blake2_128Concat, T::CrossAccountId>,
91 >;91 >;
9292
93 #[pallet::storage]93 #[pallet::storage]
94 pub(super) type AccountBalance<T: Config> = StorageNMap<94 pub type AccountBalance<T: Config> = StorageNMap<
95 Key = (95 Key = (
96 Key<Twox64Concat, CollectionId>,96 Key<Twox64Concat, CollectionId>,
97 // Owner97 // Owner
102 >;102 >;
103103
104 #[pallet::storage]104 #[pallet::storage]
105 pub(super) type Balance<T: Config> = StorageNMap<105 pub type Balance<T: Config> = StorageNMap<
106 Key = (106 Key = (
107 Key<Twox64Concat, CollectionId>,107 Key<Twox64Concat, CollectionId>,
108 Key<Twox64Concat, TokenId>,108 Key<Twox64Concat, TokenId>,
114 >;114 >;
115115
116 #[pallet::storage]116 #[pallet::storage]
117 pub(super) type Allowance<T: Config> = StorageNMap<117 pub type Allowance<T: Config> = StorageNMap<
118 Key = (118 Key = (
119 Key<Twox64Concat, CollectionId>,119 Key<Twox64Concat, CollectionId>,
120 Key<Twox64Concat, TokenId>,120 Key<Twox64Concat, TokenId>,
156156
157// unchecked calls skips any permission checks157// unchecked calls skips any permission checks
158impl<T: Config> Pallet<T> {158impl<T: Config> Pallet<T> {
159 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {159 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
160 PalletCommon::init_collection(data)160 <PalletCommon<T>>::init_collection(data)
161 }161 }
162 pub fn destroy_collection(162 pub fn destroy_collection(
163 collection: RefungibleHandle<T>,163 collection: RefungibleHandle<T>,
addedprimitives/evm-mapping/Cargo.tomldiffbeforeafterboth

no changes

addedprimitives/evm-mapping/src/lib.rsdiffbeforeafterboth

no changes

modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
56
57pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;
5658
57// Schema limits59// Schema limits
58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;
207209
208#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]210#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]
209#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]211#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
210pub struct Collection<T: frame_system::Config> {212pub struct Collection<AccountId> {
211 pub owner: T::AccountId,213 pub owner: AccountId,
212 pub mode: CollectionMode,214 pub mode: CollectionMode,
213 pub access: AccessMode,215 pub access: AccessMode,
214 pub name: Vec<u16>, // 64 include null escape char216 pub name: Vec<u16>, // 64 include null escape char
217 pub mint_mode: bool,219 pub mint_mode: bool,
218 pub offchain_schema: Vec<u8>,220 pub offchain_schema: Vec<u8>,
219 pub schema_version: SchemaVersion,221 pub schema_version: SchemaVersion,
220 pub sponsorship: SponsorshipState<T::AccountId>,222 pub sponsorship: SponsorshipState<AccountId>,
221 pub limits: CollectionLimits, // Collection private restrictions223 pub limits: CollectionLimits, // Collection private restrictions
222 pub variable_on_chain_schema: Vec<u8>, //224 pub variable_on_chain_schema: Vec<u8>, //
223 pub const_on_chain_schema: Vec<u8>, //225 pub const_on_chain_schema: Vec<u8>, //
246 pub variable_data: Vec<u8>,248 pub variable_data: Vec<u8>,
247}249}
248250
251/// All fields are wrapped in `Option`s, where None means chain default
249#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]252#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]
250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]253#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
251pub struct CollectionLimits {254pub struct CollectionLimits {
254 /// None - setVariableMetadata is not sponsored257 /// None - setVariableMetadata is not sponsored
255 /// Some(v) - setVariableMetadata is sponsored258 /// Some(v) - setVariableMetadata is sponsored
256 /// if there is v block between txs259 /// if there is v block between txs
257 pub sponsored_data_rate_limit: Option<u32>,260 pub sponsored_data_rate_limit: Option<(Option<u32>,)>,
258 pub token_limit: Option<u32>,261 pub token_limit: Option<u32>,
259262
260 // Timeouts for item types in passed blocks263 // Timeouts for item types in passed blocks
261 pub sponsor_transfer_timeout: Option<u32>,264 pub sponsor_transfer_timeout: Option<u32>,
265 pub sponsor_approve_timeout: Option<u32>,
262 pub owner_can_transfer: Option<bool>,266 pub owner_can_transfer: Option<bool>,
263 pub owner_can_destroy: Option<bool>,267 pub owner_can_destroy: Option<bool>,
264 pub transfers_enabled: Option<bool>,268 pub transfers_enabled: Option<bool>,
285 .unwrap_or(default)289 .unwrap_or(default)
286 .min(MAX_SPONSOR_TIMEOUT)290 .min(MAX_SPONSOR_TIMEOUT)
287 }291 }
292 pub fn sponsor_approve_timeout(&self) -> u32 {
293 self.sponsor_approve_timeout
294 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)
295 .min(MAX_SPONSOR_TIMEOUT)
296 }
288 pub fn owner_can_transfer(&self) -> bool {297 pub fn owner_can_transfer(&self) -> bool {
289 self.owner_can_transfer.unwrap_or(true)298 self.owner_can_transfer.unwrap_or(true)
290 }299 }
296 }305 }
297 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {306 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {
298 self.sponsored_data_rate_limit307 self.sponsored_data_rate_limit
308 .unwrap_or((None,))
309 .0
299 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))310 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))
300 }311 }
301}312}
414 }425 }
415}426}
427
428#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]
429#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
430pub struct CollectionStats {
431 pub created: u32,
432 pub destroyed: u32,
433 pub alive: u32,
434}
416435
modifiedprimitives/rpc/src/lib.rsdiffbeforeafterboth
1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]
22
3use nft_data_structs::{CollectionId, TokenId};3use nft_data_structs::{CollectionId, TokenId, Collection, CollectionStats};
4use sp_std::vec::Vec;4use sp_std::vec::Vec;
5use sp_core::H160;5use sp_core::H160;
6use codec::Decode;6use codec::Decode;
3232
33 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId>;33 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId>;
34 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId>;34 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId>;
35 fn allowed(collection: CollectionId, user: CrossAccountId) -> bool;
35 fn last_token_id(collection: CollectionId) -> TokenId;36 fn last_token_id(collection: CollectionId) -> TokenId;
37 fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>>;
38 fn collection_stats() -> CollectionStats;
36 }39 }
37}40}
3841
modifiedruntime/Cargo.tomldiffbeforeafterboth
69 'pallet-ethereum/std',69 'pallet-ethereum/std',
70 'fp-rpc/std',70 'fp-rpc/std',
71 'up-rpc/std',71 'up-rpc/std',
72 'up-evm-mapping/std',
72 'fp-self-contained/std',73 'fp-self-contained/std',
73 'parachain-info/std',74 'parachain-info/std',
74 'serde',75 'serde',
80 'pallet-nft/std',81 'pallet-nft/std',
81 'pallet-unq-scheduler/std',82 'pallet-unq-scheduler/std',
82 'pallet-nft-charge-transaction/std',83 'pallet-nft-charge-transaction/std',
83 'pallet-nft-transaction-payment/std',
84 'nft-data-structs/std',84 'nft-data-structs/std',
85 'sp-api/std',85 'sp-api/std',
86 'sp-block-builder/std',86 'sp-block-builder/std',
362default-features = false362default-features = false
363363
364[dependencies.orml-vesting]364[dependencies.orml-vesting]
365git = "https://github.com/open-web3-stack/open-runtime-module-library"365git = 'https://github.com/UniqueNetwork/open-runtime-module-library'
366version = "0.4.1-dev" 366version = "0.4.1-dev"
367default-features = false367default-features = false
368368
376derivative = "2.2.0"376derivative = "2.2.0"
377pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }377pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
378up-rpc = { path = "../primitives/rpc", default-features = false }378up-rpc = { path = "../primitives/rpc", default-features = false }
379up-evm-mapping = { path = "../primitives/evm-mapping", default-features = false }
379pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }380pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
380nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }381nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
381pallet-common = { default-features = false, path = "../pallets/common" }382pallet-common = { default-features = false, path = "../pallets/common" }
384pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }385pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }
385pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }386pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }
386# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }387# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
387pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
388pallet-nft-charge-transaction = {git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }388pallet-nft-charge-transaction = { git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }
389pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }389pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }
390pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }390pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }
modifiedruntime/src/lib.rsdiffbeforeafterboth
736736
737impl pallet_common::Config for Runtime {737impl pallet_common::Config for Runtime {
738 type Event = Event;738 type Event = Event;
739 type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;739 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;
742742
771 type InflationBlockInterval = InflationBlockInterval;771 type InflationBlockInterval = InflationBlockInterval;
772}772}
773773
774parameter_types! {774// parameter_types! {
775 pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *775// pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
776 RuntimeBlockWeights::get().max_block;776// RuntimeBlockWeights::get().max_block;
777 pub const MaxScheduledPerBlock: u32 = 50;777// pub const MaxScheduledPerBlock: u32 = 50;
778}778// }
779779
780pub struct Sponsoring;
781impl SponsoringResolve<AccountId, Call> for Sponsoring {
782 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>
783 where
784 Call: Dispatchable<Info = DispatchInfo>,780type EvmSponsorshipHandler = (
785 AccountId: AsRef<[u8]>,781 pallet_nft::NftEthSponsorshipHandler<Runtime>,
786 {
787 pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)782 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
788 }783);
789}
790
791type SponsorshipHandler = (784type SponsorshipHandler = (
792 pallet_nft::NftSponsorshipHandler<Runtime>,785 pallet_nft::NftSponsorshipHandler<Runtime>,
793 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,786 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
787 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
794);788);
789
790// impl pallet_unq_scheduler::Config for Runtime {
791// type Event = Event;
792// type Origin = Origin;
793// type PalletsOrigin = OriginCaller;
794// type Call = Call;
795// type MaximumWeight = MaximumSchedulerWeight;
796// type ScheduleOrigin = EnsureSigned<AccountId>;
797// type MaxScheduledPerBlock = MaxScheduledPerBlock;
798// type SponsorshipHandler = SponsorshipHandler;
799// type WeightInfo = ();
800// }
795801
796impl pallet_unq_scheduler::Config for Runtime {802impl pallet_evm_transaction_payment::Config for Runtime {
797 type Event = Event;803 type EvmSponsorshipHandler = EvmSponsorshipHandler;
798 type Origin = Origin;804 type Currency = Balances;
799 type PalletsOrigin = OriginCaller;
800 type Call = Call;
801 type MaximumWeight = MaximumSchedulerWeight;
802 type ScheduleOrigin = EnsureSigned<AccountId>;805 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
803 type MaxScheduledPerBlock = MaxScheduledPerBlock;806 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
804 type SponsorshipHandler = SponsorshipHandler;
805 type WeightInfo = ();
806}807}
807808
808impl pallet_nft_transaction_payment::Config for Runtime {809impl pallet_nft_charge_transaction::Config for Runtime {
809 type SponsorshipHandler = SponsorshipHandler;810 type SponsorshipHandler = SponsorshipHandler;
810}811}
811
812impl pallet_evm_transaction_payment::Config for Runtime {
813 type SponsorshipHandler = (
814 pallet_nft::NftEthSponsorshipHandler<Self>,
815 pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,
816 );
817 type Currency = Balances;
818}
819
820impl pallet_nft_charge_transaction::Config for Runtime {
821 type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;
822}
823812
824// impl pallet_contract_helpers::Config for Runtime {813// impl pallet_contract_helpers::Config for Runtime {
825// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;814// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;
869 // Unique Pallets858 // Unique Pallets
870 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,859 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
871 Nft: pallet_nft::{Pallet, Call, Storage} = 61,860 Nft: pallet_nft::{Pallet, Call, Storage} = 61,
872 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,861 // Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
873 NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,862 // free = 63
874 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,863 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,
875 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,864 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
876 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,865 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,
1056 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))1045 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))
1057 }1046 }
1058 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {1047 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {
1059 <pallet_nft::Pallet<Runtime>>::adminlist(collection)1048 <pallet_common::Pallet<Runtime>>::adminlist(collection)
1060 }1049 }
1061 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {1050 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {
1062 <pallet_nft::Pallet<Runtime>>::allowlist(collection)1051 <pallet_common::Pallet<Runtime>>::allowlist(collection)
1063 }1052 }
1053 fn allowed(collection: CollectionId, user: CrossAccountId) -> bool {
1054 <pallet_common::Pallet<Runtime>>::allowed(collection, user)
1055 }
1064 fn last_token_id(collection: CollectionId) -> TokenId {1056 fn last_token_id(collection: CollectionId) -> TokenId {
1065 dispatch_nft_runtime!(collection.last_token_id())1057 dispatch_nft_runtime!(collection.last_token_id())
1066 }1058 }
1059 fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>> {
1060 <pallet_common::CollectionById<Runtime>>::get(collection)
1061 }
1062 fn collection_stats() -> CollectionStats {
1063 <pallet_common::Pallet<Runtime>>::collection_stats()
1064 }
1067 }1065 }
10681066
1069 impl sp_api::Core<Block> for Runtime {1067 impl sp_api::Core<Block> for Runtime {
modifiedtests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
8import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';
9import privateKey from './substrate/privateKey';9import privateKey from './substrate/privateKey';
10import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';10import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
11import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId} from './util/helpers';11import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId, queryCollectionExpectSuccess} from './util/helpers';
1212
13chai.use(chaiAsPromised);13chai.use(chaiAsPromised);
14const expect = chai.expect;14const expect = chai.expect;
20 const alice = privateKey('//Alice');20 const alice = privateKey('//Alice');
21 const bob = privateKey('//Bob');21 const bob = privateKey('//Bob');
2222
23 const collection = (await api.query.common.collectionById(collectionId)).unwrap();23 const collection = await queryCollectionExpectSuccess(api, collectionId);
24 expect(collection.owner.toString()).to.be.equal(alice.address);24 expect(collection.owner.toString()).to.be.equal(alice.address);
2525
26 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));26 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
38 const bob = privateKey('//Bob');38 const bob = privateKey('//Bob');
39 const charlie = privateKey('//CHARLIE');39 const charlie = privateKey('//CHARLIE');
4040
41 const collection = (await api.query.common.collectionById(collectionId)).unwrap();41 const collection = await queryCollectionExpectSuccess(api, collectionId);
42 expect(collection.owner.toString()).to.be.equal(alice.address);42 expect(collection.owner.toString()).to.be.equal(alice.address);
4343
44 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));44 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
modifiedtests/src/addToAllowList.test.tsdiffbeforeafterboth
18 normalizeAccountId,18 normalizeAccountId,
19 addCollectionAdminExpectSuccess,19 addCollectionAdminExpectSuccess,
20 addToAllowListExpectFail,20 addToAllowListExpectFail,
21 getCreatedCollectionCount,
21} from './util/helpers';22} from './util/helpers';
2223
23chai.use(chaiAsPromised);24chai.use(chaiAsPromised);
55 it('Allow list an address in the collection that does not exist', async () => {56 it('Allow list an address in the collection that does not exist', async () => {
56 await usingApi(async (api) => {57 await usingApi(async (api) => {
57 // tslint:disable-next-line: no-bitwise58 // tslint:disable-next-line: no-bitwise
58 const collectionId = ((await api.query.common.createdCollectionCount()).toNumber()) + 1;59 const collectionId = await getCreatedCollectionCount(api) + 1;
59 const bob = privateKey('//Bob');60 const bob = privateKey('//Bob');
6061
61 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(bob.address));62 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(bob.address));
modifiedtests/src/approve.test.tsdiffbeforeafterboth
18 transferExpectSuccess,18 transferExpectSuccess,
19 addCollectionAdminExpectSuccess,19 addCollectionAdminExpectSuccess,
20 adminApproveFromExpectSuccess,20 adminApproveFromExpectSuccess,
21 getCreatedCollectionCount,
22 transferFromExpectSuccess,
23 transferFromExpectFail,
21} from './util/helpers';24} from './util/helpers';
2225
23chai.use(chaiAsPromised);26chai.use(chaiAsPromised);
27const expect = chai.expect;
2428
25describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {29describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
26 let alice: IKeyringPair;30 let alice: IKeyringPair;
78 });82 });
79});83});
8084
85describe('Normal user can approve other users to transfer:', () => {
86 let alice: IKeyringPair;
87 let bob: IKeyringPair;
88 let charlie: IKeyringPair;
89
90 before(async () => {
91 await usingApi(async () => {
92 alice = privateKey('//Alice');
93 bob = privateKey('//Bob');
94 charlie = privateKey('//Charlie');
95 });
96 });
97
98 it('NFT', async () => {
99 const collectionId = await createCollectionExpectSuccess();
100 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
101 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
102 });
103
104 it('Fungible up to an approved amount', async () => {
105 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
106 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
107 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
108 });
109
110 it('ReFungible up to an approved amount', async () => {
111 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
112 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
113 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
114 });
115});
116
117describe('Approved users can transferFrom up to approved amount:', () => {
118 let alice: IKeyringPair;
119 let bob: IKeyringPair;
120 let charlie: IKeyringPair;
121
122 before(async () => {
123 await usingApi(async () => {
124 alice = privateKey('//Alice');
125 bob = privateKey('//Bob');
126 charlie = privateKey('//Charlie');
127 });
128 });
129
130 it('NFT', async () => {
131 const collectionId = await createCollectionExpectSuccess();
132 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
133 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
134 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');
135 });
136
137 it('Fungible up to an approved amount', async () => {
138 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
139 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
140 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
141 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');
142 });
143
144 it('ReFungible up to an approved amount', async () => {
145 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
146 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
147 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
148 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');
149 });
150});
151
152describe('Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:', () => {
153 let alice: IKeyringPair;
154 let bob: IKeyringPair;
155 let charlie: IKeyringPair;
156
157 before(async () => {
158 await usingApi(async () => {
159 alice = privateKey('//Alice');
160 bob = privateKey('//Bob');
161 charlie = privateKey('//Charlie');
162 });
163 });
164
165 it('NFT', async () => {
166 const collectionId = await createCollectionExpectSuccess();
167 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
168 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
169 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');
170 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
171 });
172
173 it('Fungible up to an approved amount', async () => {
174 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
175 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
176 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
177 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');
178 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
179 });
180
181 it('ReFungible up to an approved amount', async () => {
182 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
183 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
184 await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
185 await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');
186 await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
187 });
188});
189
190describe('Approved amount decreases by the transferred amount.:', () => {
191 let alice: IKeyringPair;
192 let bob: IKeyringPair;
193 let charlie: IKeyringPair;
194 let dave: IKeyringPair;
195
196 before(async () => {
197 await usingApi(async () => {
198 alice = privateKey('//Alice');
199 bob = privateKey('//Bob');
200 charlie = privateKey('//Charlie');
201 dave = privateKey('//Dave');
202 });
203 });
204
205 it('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async () => {
206 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
207 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);
208 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 10);
209 await transferFromExpectSuccess(collectionId, itemId, bob, alice, charlie, 2, 'Fungible');
210 await transferFromExpectSuccess(collectionId, itemId, bob, alice, dave, 8, 'Fungible');
211 });
212});
213
214describe('User may clear the approvals to approving for 0 amount:', () => {
215 let alice: IKeyringPair;
216 let bob: IKeyringPair;
217 let charlie: IKeyringPair;
218
219 before(async () => {
220 await usingApi(async () => {
221 alice = privateKey('//Alice');
222 bob = privateKey('//Bob');
223 charlie = privateKey('//Charlie');
224 });
225 });
226
227 it('NFT', async () => {
228 const collectionId = await createCollectionExpectSuccess();
229 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT');
230 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);
231 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 0);
232 await transferFromExpectFail(collectionId, itemId, bob, bob, charlie, 1);
233 });
234
235 it('Fungible', async () => {
236 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
237 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
238 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);
239 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);
240 await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, bob, charlie, 1);
241 });
242
243 it('ReFungible', async () => {
244 const reFungibleCollectionId =
245 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
246 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
247 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);
248 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);
249 await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, bob, charlie, 1);
250 });
251});
252
253describe('User cannot approve for the amount greater than they own:', () => {
254 let alice: IKeyringPair;
255 let bob: IKeyringPair;
256 let charlie: IKeyringPair;
257
258 before(async () => {
259 await usingApi(async () => {
260 alice = privateKey('//Alice');
261 bob = privateKey('//Bob');
262 charlie = privateKey('//Charlie');
263 });
264 });
265
266 it('1 for NFT', async () => {
267 const collectionId = await createCollectionExpectSuccess();
268 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
269 await approveExpectFail(collectionId, itemId, bob, charlie, 2);
270 });
271
272 it('Fungible', async () => {
273 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
274 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
275 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, charlie, 11);
276 });
277
278 it('ReFungible', async () => {
279 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
280 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
281 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, charlie, 101);
282 });
283});
284
285describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {
286 let alice: IKeyringPair;
287 let bob: IKeyringPair;
288 let charlie: IKeyringPair;
289 let dave: IKeyringPair;
290
291 before(async () => {
292 await usingApi(async () => {
293 alice = privateKey('//Alice');
294 bob = privateKey('//Bob');
295 charlie = privateKey('//Charlie');
296 dave = privateKey('//Dave');
297 });
298 });
299
300 it('NFT', async () => {
301 const collectionId = await createCollectionExpectSuccess();
302 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);
303 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');
304 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
305 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'NFT');
306 });
307
308 it('Fungible up to an approved amount', async () => {
309 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
310 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address);
311 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');
312 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
313 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'Fungible');
314 });
315
316 it('ReFungible up to an approved amount', async () => {
317 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
318 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);
319 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');
320 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
321 await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'ReFungible');
322 });
323});
324
325describe('Repeated approvals add up', () => {
326 let alice: IKeyringPair;
327 let bob: IKeyringPair;
328 let charlie: IKeyringPair;
329 let dave: IKeyringPair;
330
331 before(async () => {
332 await usingApi(async () => {
333 alice = privateKey('//Alice');
334 bob = privateKey('//Bob');
335 charlie = privateKey('//Charlie');
336 dave = privateKey('//Dave');
337 });
338 });
339
340 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async () => {
341 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
342 await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);
343 await approveExpectSuccess(collectionId, 0, alice, bob.address, 1);
344 await approveExpectSuccess(collectionId, 0, alice, charlie.address, 1);
345 // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address);
346 // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address);
347 // expect(allowances1 + allowances2).to.be.eq(BigInt(2));
348 });
349
350 it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async () => {
351 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
352 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);
353 await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);
354 await approveExpectSuccess(collectionId, itemId, alice, charlie.address, 1);
355 // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address);
356 // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address);
357 // expect(allowances1 + allowances2).to.be.eq(BigInt(2));
358 });
359
360 // Canceled by changing approve logic
361 it.skip('Cannot approve for more than total user`s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async () => {
362 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
363 await createItemExpectSuccess(alice, collectionId, 'Fungible', dave.address);
364 await approveExpectSuccess(collectionId, 0, dave, bob.address, 5);
365 await approveExpectFail(collectionId, 0, dave, charlie, 6);
366 });
367
368 // Canceled by changing approve logic
369 it.skip('Cannot approve for more than total users amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async () => {
370 const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
371 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', dave.address);
372 await approveExpectSuccess(collectionId, itemId, dave, bob.address, 50);
373 await approveExpectFail(collectionId, itemId, dave, charlie, 51);
374 });
375});
376
377describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {
378 let alice: IKeyringPair;
379 let bob: IKeyringPair;
380 let charlie: IKeyringPair;
381
382 before(async () => {
383 await usingApi(async () => {
384 alice = privateKey('//Alice');
385 bob = privateKey('//Bob');
386 charlie = privateKey('//Charlie');
387 });
388 });
389
390 it('can be called by collection admin on non-owned item', async () => {
391 const collectionId = await createCollectionExpectSuccess();
392 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
393
394 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
395 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);
396 });
397});
398
81describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {399describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
82 let alice: IKeyringPair;400 let alice: IKeyringPair;
94 it('Approve for a collection that does not exist', async () => {412 it('Approve for a collection that does not exist', async () => {
95 await usingApi(async (api: ApiPromise) => {413 await usingApi(async (api: ApiPromise) => {
96 // nft414 // nft
97 const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();415 const nftCollectionCount = await getCreatedCollectionCount(api);
98 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);416 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);
99 // fungible417 // fungible
100 const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();418 const fungibleCollectionCount = await getCreatedCollectionCount(api);
101 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);419 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);
102 // reFungible420 // reFungible
103 const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();421 const reFungibleCollectionCount = await getCreatedCollectionCount(api);
104 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);422 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);
105 });423 });
106 });424 });
169 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});487 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});
170488
171 await approveExpectFail(collectionId, itemId, alice, charlie);489 await approveExpectFail(collectionId, itemId, alice, charlie);
172 });
173});
174
175describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {
176 let alice: IKeyringPair;
177 let bob: IKeyringPair;
178 let charlie: IKeyringPair;
179
180 before(async () => {
181 await usingApi(async () => {
182 alice = privateKey('//Alice');
183 bob = privateKey('//Bob');
184 charlie = privateKey('//Charlie');
185 });
186 });
187
188 it('can be called by collection admin on non-owned item', async () => {
189 const collectionId = await createCollectionExpectSuccess();
190 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
191
192 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
193 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);
194 });490 });
195});491});
196492
modifiedtests/src/change-collection-owner.test.tsdiffbeforeafterboth
22 setMintPermissionExpectFailure,22 setMintPermissionExpectFailure,
23 destroyCollectionExpectFailure,23 destroyCollectionExpectFailure,
24 setPublicAccessModeExpectSuccess,24 setPublicAccessModeExpectSuccess,
25 queryCollectionExpectSuccess,
25} from './util/helpers';26} from './util/helpers';
2627
27chai.use(chaiAsPromised);28chai.use(chaiAsPromised);
34 const alice = privateKey('//Alice');35 const alice = privateKey('//Alice');
35 const bob = privateKey('//Bob');36 const bob = privateKey('//Bob');
3637
37 const collection = (await api.query.common.collectionById(collectionId)).unwrap();38 const collection =await queryCollectionExpectSuccess(api, collectionId);
38 expect(collection.owner.toString()).to.be.deep.eq(alice.address);39 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
3940
40 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);41 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
41 await submitTransactionAsync(alice, changeOwnerTx);42 await submitTransactionAsync(alice, changeOwnerTx);
4243
43 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();44 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
44 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);45 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
45 });46 });
46 });47 });
53 const alice = privateKey('//Alice');54 const alice = privateKey('//Alice');
54 const bob = privateKey('//Bob');55 const bob = privateKey('//Bob');
5556
56 const collection = (await api.query.common.collectionById(collectionId)).unwrap();57 const collection = await queryCollectionExpectSuccess(api, collectionId);
57 expect(collection.owner.toString()).to.be.deep.eq(alice.address);58 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
5859
59 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);60 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
62 const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);63 const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
63 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;64 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
6465
65 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();66 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
66 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);67 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
67 });68 });
68 });69 });
74 const bob = privateKey('//Bob');75 const bob = privateKey('//Bob');
75 const charlie = privateKey('//Charlie');76 const charlie = privateKey('//Charlie');
7677
77 const collection = (await api.query.common.collectionById(collectionId)).unwrap();78 const collection = await queryCollectionExpectSuccess(api, collectionId);
78 expect(collection.owner.toString()).to.be.deep.eq(alice.address);79 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
7980
80 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);81 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
81 await submitTransactionAsync(alice, changeOwnerTx);82 await submitTransactionAsync(alice, changeOwnerTx);
8283
83 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();84 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
84 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);85 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
8586
86 // After changing the owner of the collection, all privileged methods are available to the new owner87 // After changing the owner of the collection, all privileged methods are available to the new owner
118 const bob = privateKey('//Bob');119 const bob = privateKey('//Bob');
119 const charlie = privateKey('//Charlie');120 const charlie = privateKey('//Charlie');
120121
121 const collection = (await api.query.common.collectionById(collectionId)).unwrap();122 const collection = await queryCollectionExpectSuccess(api, collectionId);
122 expect(collection.owner.toString()).to.be.deep.eq(alice.address);123 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
123124
124 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);125 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
125 await submitTransactionAsync(alice, changeOwnerTx);126 await submitTransactionAsync(alice, changeOwnerTx);
126127
127 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();128 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
128 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);129 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
129130
130 const changeOwnerTx2 = api.tx.nft.changeCollectionOwner(collectionId, charlie.address);131 const changeOwnerTx2 = api.tx.nft.changeCollectionOwner(collectionId, charlie.address);
131 await submitTransactionAsync(bob, changeOwnerTx2);132 await submitTransactionAsync(bob, changeOwnerTx2);
132133
133 // ownership lost134 // ownership lost
134 const collectionAfterOwnerChange2 = (await api.query.common.collectionById(collectionId)).unwrap();135 const collectionAfterOwnerChange2 = await queryCollectionExpectSuccess(api, collectionId);
135 expect(collectionAfterOwnerChange2.owner.toString()).to.be.deep.eq(charlie.address);136 expect(collectionAfterOwnerChange2.owner.toString()).to.be.deep.eq(charlie.address);
136 });137 });
137 });138 });
147 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);148 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
148 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;149 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;
149150
150 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();151 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
151 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);152 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
152153
153 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)154 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
166 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);167 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
167 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;168 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;
168169
169 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();170 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
170 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);171 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
171172
172 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)173 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
195 const bob = privateKey('//Bob');196 const bob = privateKey('//Bob');
196 const charlie = privateKey('//Charlie');197 const charlie = privateKey('//Charlie');
197198
198 const collection = (await api.query.common.collectionById(collectionId)).unwrap();199 const collection = await queryCollectionExpectSuccess(api, collectionId);
199 expect(collection.owner.toString()).to.be.deep.eq(alice.address);200 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
200201
201 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);202 const changeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, bob.address);
204 const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);205 const badChangeOwnerTx = api.tx.nft.changeCollectionOwner(collectionId, alice.address);
205 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;206 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;
206207
207 const collectionAfterOwnerChange = (await api.query.common.collectionById(collectionId)).unwrap();208 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
208 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);209 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
209210
210 await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Alice');211 await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Alice');
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
20 addToAllowListExpectSuccess,20 addToAllowListExpectSuccess,
21 normalizeAccountId,21 normalizeAccountId,
22 addCollectionAdminExpectSuccess,22 addCollectionAdminExpectSuccess,
23 getCreatedCollectionCount,
23} from './util/helpers';24} from './util/helpers';
24import {Keyring} from '@polkadot/api';25import {Keyring} from '@polkadot/api';
25import {IKeyringPair} from '@polkadot/types/types';26import {IKeyringPair} from '@polkadot/types/types';
251 const zeroBalance = await findUnusedAddress(api);252 const zeroBalance = await findUnusedAddress(api);
252253
253 // Mint token for alice254 // Mint token for alice
254 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);255 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
255256
256 // Transfer this token from Alice to unused address and back
257 // Alice to Zero gets sponsored
258 const aliceToZero = api.tx.nft.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);257 const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
258
259 // Zero to alice gets sponsored
259 const events1 = await submitTransactionAsync(alice, aliceToZero);260 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
260 const result1 = getGenericResult(events1);261 const result1 = getGenericResult(events1);
261 expect(result1.success).to.be.true;262 expect(result1.success).to.be.true;
262263
263 // Second transfer should fail264 // Second transfer should fail
264 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();265 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
265 const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
266 const badTransaction = async function () {
267 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
268 };
269 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');266 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');
270 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();267 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
268 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
271269
272 // Try again after Zero gets some balance - now it should succeed270 // Try again after Zero gets some balance - now it should succeed
273 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);271 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
276 const result2 = getGenericResult(events2);274 const result2 = getGenericResult(events2);
277 expect(result2.success).to.be.true;275 expect(result2.success).to.be.true;
278
279 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
280 });276 });
281 });277 });
282278
335 // Find the collection that never existed331 // Find the collection that never existed
336 let collectionId = 0;332 let collectionId = 0;
337 await usingApi(async (api) => {333 await usingApi(async (api) => {
338 collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;334 collectionId = await getCreatedCollectionCount(api) + 1;
339 });335 });
340336
341 await confirmSponsorshipExpectFailure(collectionId, '//Bob');337 await confirmSponsorshipExpectFailure(collectionId, '//Bob');
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
228 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);228 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);
229 await submitTransactionAsync(alice, changeAdminTx);229 await submitTransactionAsync(alice, changeAdminTx);
230230
231 expect(await isAllowlisted(collectionId, bob.address)).to.be.false;231 expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
232232
233 {233 {
234 const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, true);234 const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, true);
235 const events = await submitTransactionAsync(alice, transferTx);235 const events = await submitTransactionAsync(alice, transferTx);
236 const result = getGenericResult(events);236 const result = getGenericResult(events);
237 expect(result.success).to.be.true;237 expect(result.success).to.be.true;
238238
239 expect(await isAllowlisted(collectionId, bob.address)).to.be.true;239 expect(await isAllowlisted(api, collectionId, bob.address)).to.be.true;
240 }240 }
241 {241 {
242 const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, false);242 const transferTx = contract.tx.toggleAllowList(value, gasLimit, collectionId, bob.address, false);
243 const events = await submitTransactionAsync(alice, transferTx);243 const events = await submitTransactionAsync(alice, transferTx);
244 const result = getGenericResult(events);244 const result = getGenericResult(events);
245 expect(result.success).to.be.true;245 expect(result.success).to.be.true;
246246
247 expect(await isAllowlisted(collectionId, bob.address)).to.be.false;247 expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
248 }248 }
249 });249 });
250 });250 });
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
20 getLastTokenId,20 getLastTokenId,
21 getVariableMetadata,21 getVariableMetadata,
22 getConstMetadata,22 getConstMetadata,
23 getCreatedCollectionCount,
23} from './util/helpers';24} from './util/helpers';
2425
25chai.use(chaiAsPromised);26chai.use(chaiAsPromised);
273274
274 it('Create token in not existing collection', async () => {275 it('Create token in not existing collection', async () => {
275 await usingApi(async (api: ApiPromise) => {276 await usingApi(async (api: ApiPromise) => {
276 const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;277 const collectionId = await getCreatedCollectionCount(api) + 1;
277 const createMultipleItemsTx = api.tx.nft278 const createMultipleItemsTx = api.tx.nft
278 .createMultipleItems(collectionId, normalizeAccountId(alice.address), ['NFT', 'NFT', 'NFT']);279 .createMultipleItems(collectionId, normalizeAccountId(alice.address), ['NFT', 'NFT', 'NFT']);
279 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;280 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;
modifiedtests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth
14 createItemExpectSuccess,14 createItemExpectSuccess,
15 getGenericResult,15 getGenericResult,
16 transferExpectSuccess,16 transferExpectSuccess,
17 UNIQUE,
17} from './util/helpers';18} from './util/helpers';
1819
19import {default as waitNewBlocks} from './substrate/wait-new-blocks';20import {default as waitNewBlocks} from './substrate/wait-new-blocks';
169 const aliceBalanceBefore: bigint = (await api.query.system.account(alicesPublicKey)).data.free.toBigInt();170 const aliceBalanceBefore: bigint = (await api.query.system.account(alicesPublicKey)).data.free.toBigInt();
170 await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');171 await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');
171 const aliceBalanceAfter: bigint = (await api.query.system.account(alicesPublicKey)).data.free.toBigInt();172 const aliceBalanceAfter: bigint = (await api.query.system.account(alicesPublicKey)).data.free.toBigInt();
172 const fee = aliceBalanceBefore - aliceBalanceAfter;173 const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(UNIQUE);
173174
174 // console.log(fee.toString());
175 const expectedTransferFee = 0.1;175 const expectedTransferFee = 0.1;
176 const tolerance = 0.001;176 const tolerance = 0.001;
177 expect(Number(fee) / 1e15 - expectedTransferFee).to.be.lessThan(tolerance);177 expect(Number(fee) / Number(UNIQUE) - expectedTransferFee).to.be.lessThan(tolerance);
178 });178 });
179 });179 });
180180
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
13 destroyCollectionExpectFailure,13 destroyCollectionExpectFailure,
14 setCollectionLimitsExpectSuccess,14 setCollectionLimitsExpectSuccess,
15 addCollectionAdminExpectSuccess,15 addCollectionAdminExpectSuccess,
16 getCreatedCollectionCount,
16} from './util/helpers';17} from './util/helpers';
1718
18chai.use(chaiAsPromised);19chai.use(chaiAsPromised);
46 it('(!negative test!) Destroy a collection that never existed', async () => {47 it('(!negative test!) Destroy a collection that never existed', async () => {
47 await usingApi(async (api) => {48 await usingApi(async (api) => {
48 // Find the collection that never existed49 // Find the collection that never existed
49 const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;50 const collectionId = await getCreatedCollectionCount(api) + 1;
50 await destroyCollectionExpectFailure(collectionId);51 await destroyCollectionExpectFailure(collectionId);
51 });52 });
52 });53 });
modifiedtests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
2import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';2import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';
3import privateKey from '../../substrate/privateKey';3import privateKey from '../../substrate/privateKey';
4import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';4import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';
5import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase} from '../util/helpers';5import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase, transferBalanceToEth} from '../util/helpers';
6import {evmToAddress} from '@polkadot/util-crypto';6import {evmToAddress} from '@polkadot/util-crypto';
7import nonFungibleAbi from '../nonFungibleAbi.json';7import nonFungibleAbi from '../nonFungibleAbi.json';
8import fungibleAbi from '../fungibleAbi.json';8import fungibleAbi from '../fungibleAbi.json';
1212
13describe('Matcher contract usage', () => {13describe('Matcher contract usage', () => {
14 itWeb3('With UNQ', async ({api, web3}) => {14 itWeb3('With UNQ', async ({api, web3}) => {
15 const alice = privateKey('//Alice');
16 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
17 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
15 const matcherOwner = await createEthAccountWithBalance(api, web3);18 const matcherOwner = await createEthAccountWithBalance(api, web3);
19 const helpers = contractHelpers(web3, matcherOwner);
20
16 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {21 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
17 from: matcherOwner,22 from: matcherOwner,
18 ...GAS_ARGS,23 ...GAS_ARGS,
19 });24 });
20 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});25 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});
21 const helpers = contractHelpers(web3, matcherOwner);26
22 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});27 await transferBalanceToEth(api, alice, matcher.options.address);
23 await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});28 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
2429
25 const alice = privateKey('//Alice');
26 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});30 await transferBalanceToEth(api, alice, subToEth(alice.address));
27 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
28 await setCollectionSponsorExpectSuccess(collectionId, alice.address);31 await setCollectionSponsorExpectSuccess(collectionId, alice.address);
29 await confirmSponsorshipExpectSuccess(collectionId);32 await confirmSponsorshipExpectSuccess(collectionId);
3033
31 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(alice.address), true).send({from: matcherOwner});34 const seller = privateKey('//Seller/' + Date.now());
32 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(alice.address)));35 await addToAllowListExpectSuccess(alice, collectionId, {Ethereum:subToEth(seller.address)});
33
34 const seller = privateKey('//Bob');
35 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});36 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
36 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
3737
38 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);38 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
3939
40 // To transfer item to matcher it first needs to be transfered to EVM account of bob40 // To transfer item to matcher it first needs to be transfered to EVM account of seller
41 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});41 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
42
43 // Token is owned by seller initially
44 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});42 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
4543
46 // Ask44 // Ask
55 // Buy53 // Buy
56 {54 {
57 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);55 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);
58 // There is two functions named 'buy', so we should provide full signature
59 await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), {value: PRICE});56 await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId), {value: PRICE});
60 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);57 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);
61 }58 }
6259
137 });133 });
138134
139 itWeb3('With escrow', async ({api, web3}) => {135 itWeb3('With escrow', async ({api, web3}) => {
136 const alice = privateKey('//Alice');
137 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
138 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
140 const matcherOwner = await createEthAccountWithBalance(api, web3);139 const matcherOwner = await createEthAccountWithBalance(api, web3);
140 const helpers = contractHelpers(web3, matcherOwner);
141 const escrow = await createEthAccountWithBalance(api, web3);141 const escrow = await createEthAccountWithBalance(api, web3);
142
142 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {143 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
143 from: matcherOwner,144 from: matcherOwner,
144 ...GAS_ARGS,145 ...GAS_ARGS,
145 });146 });
146 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});147 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});
147 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});148 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});
148 const helpers = contractHelpers(web3, matcherOwner);149
149 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});150 await transferBalanceToEth(api, alice, matcher.options.address);
150 await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});151 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
151152
152 const alice = privateKey('//Alice');
153 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});153 await transferBalanceToEth(api, alice, subToEth(alice.address));
154 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
155 await setCollectionSponsorExpectSuccess(collectionId, alice.address);154 await setCollectionSponsorExpectSuccess(collectionId, alice.address);
156 await confirmSponsorshipExpectSuccess(collectionId);155 await confirmSponsorshipExpectSuccess(collectionId);
157
158 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(alice.address), true).send({from: matcherOwner});
159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(alice.address)));
160156
161 const seller = privateKey('//Bob');157 const seller = privateKey('//Seller/' + Date.now());
162 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});158 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
163 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
164160
165 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);161 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
166162
167 // To transfer item to matcher it first needs to be transfered to EVM account of bob163 // To transfer item to matcher it first needs to be transfered to EVM account of seller
168 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});164 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
169
170 // Token is owned by seller initially
171 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});165 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
172166
173 // Ask167 // Ask
modifiedtests/src/eth/util/helpers.tsdiffbeforeafterboth
228 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});228 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});
229}229}
230230
231/**
232 * Execute ethereum method call using substrate account
233 * @param to target contract
234 * @param mkTx - closure, receiving `contract.methods`, and returning method call,
235 * to be used as following (assuming `to` = erc20 contract):
236 * `m => m.transfer(to, amount)`
237 *
238 * # Example
239 * ```ts
240 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));
241 * ```
242 */
231export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {243export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {
232 const tx = api.tx.evm.call(244 const tx = api.tx.evm.call(
233 subToEth(from.address),245 subToEth(from.address),
246 return (await getBalance(api, [evmToAddress(address)]))[0];258 return (await getBalance(api, [evmToAddress(address)]))[0];
247}259}
248260
261/**
262 * Measure how much gas given closure consumes
263 *
264 * @param user which user balance will be checked
265 */
249export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {266export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {
250 const before = await ethBalanceViaSub(api, user);267 const before = await ethBalanceViaSub(api, user);
251268
modifiedtests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth
1// Auto-generated via `yarn polkadot-types-from-chain`, do not edit1// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
2/* eslint-disable */2/* eslint-disable */
33
4import type { NftDataStructsCollectionId, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr } from './nft';4import type { NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionStats, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr } from './nft';
5import type { Bytes, HashMap, Json, Metadata, Null, Option, StorageKey, Text, U256, U64, Vec, bool, u128, u32, u64 } from '@polkadot/types';5import type { Bytes, HashMap, Json, Metadata, Null, Option, StorageKey, Text, U256, U64, Vec, bool, u128, u32, u64 } from '@polkadot/types';
6import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';6import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
7import type { EpochAuthorship } from '@polkadot/types/interfaces/babe';7import type { EpochAuthorship } from '@polkadot/types/interfaces/babe';
372 * Get allowed amount372 * Get allowed amount
373 **/373 **/
374 allowance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, sender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;374 allowance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, sender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
375 /**
376 * Check if user is allowed to use collection
377 **/
378 allowed: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, account: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
375 /**379 /**
376 * Get allowlist380 * Get allowlist
377 **/381 **/
380 * Get amount of specific account token384 * Get amount of specific account token
381 **/385 **/
382 balance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, account: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;386 balance: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, account: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: NftDataStructsTokenId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
387 /**
388 * Get collection by specified id
389 **/
390 collectionById: AugmentedRpc<(collection: NftDataStructsCollectionId | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<NftDataStructsCollection>>>;
391 /**
392 * Get collection stats
393 **/
394 collectionStats: AugmentedRpc<(at?: Hash | string | Uint8Array) => Observable<NftDataStructsCollectionStats>>;
383 /**395 /**
384 * Get tokens contained in collection396 * Get tokens contained in collection
385 **/397 **/
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
2/* eslint-disable */2/* eslint-disable */
33
4import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';4import type { EthereumBlock, EthereumLog, EthereumReceipt, EthereumTransactionLegacyTransaction, EvmCoreErrorExitReason, FpRpcTransactionStatus } from './ethereum';
5import type { NftDataStructsAccessMode, NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionLimits, NftDataStructsCollectionMode, NftDataStructsCreateItemData, NftDataStructsMetaUpdatePermission, NftDataStructsSchemaVersion, NftDataStructsSponsorshipState, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2 } from './nft';5import type { NftDataStructsAccessMode, NftDataStructsCollection, NftDataStructsCollectionId, NftDataStructsCollectionLimits, NftDataStructsCollectionMode, NftDataStructsCollectionStats, NftDataStructsCreateItemData, NftDataStructsMetaUpdatePermission, NftDataStructsSchemaVersion, NftDataStructsSponsorshipState, NftDataStructsTokenId, PalletCommonAccountBasicCrossAccountIdRepr, PalletNonfungibleItemData, PalletRefungibleItemData, PalletUnqSchedulerCallSpec, PalletUnqSchedulerReleases, PalletUnqSchedulerScheduledV2 } from './nft';
6import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';6import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundStatus, CumulusPalletXcmpQueueOutboundStatus, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1PersistedValidationData } from './polkadot';
7import type { BitVec, Bool, Bytes, Data, I128, I16, I256, I32, I64, I8, Json, Null, Raw, StorageKey, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types';7import type { BitVec, Bool, Bytes, Data, I128, I16, I256, I32, I64, I8, Json, Null, Raw, StorageKey, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types';
8import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';8import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
627 NftDataStructsCollectionId: NftDataStructsCollectionId;627 NftDataStructsCollectionId: NftDataStructsCollectionId;
628 NftDataStructsCollectionLimits: NftDataStructsCollectionLimits;628 NftDataStructsCollectionLimits: NftDataStructsCollectionLimits;
629 NftDataStructsCollectionMode: NftDataStructsCollectionMode;629 NftDataStructsCollectionMode: NftDataStructsCollectionMode;
630 NftDataStructsCollectionStats: NftDataStructsCollectionStats;
630 NftDataStructsCreateItemData: NftDataStructsCreateItemData;631 NftDataStructsCreateItemData: NftDataStructsCreateItemData;
631 NftDataStructsMetaUpdatePermission: NftDataStructsMetaUpdatePermission;632 NftDataStructsMetaUpdatePermission: NftDataStructsMetaUpdatePermission;
632 NftDataStructsSchemaVersion: NftDataStructsSchemaVersion;633 NftDataStructsSchemaVersion: NftDataStructsSchemaVersion;
modifiedtests/src/interfaces/nft/definitions.tsdiffbeforeafterboth
40 constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),40 constMetadata: fun('Get token constant metadata', [collectionParam, tokenParam], 'Vec<u8>'),
41 variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),41 variableMetadata: fun('Get token variable metadata', [collectionParam, tokenParam], 'Vec<u8>'),
42 tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),42 tokenExists: fun('Check if token exists', [collectionParam, tokenParam], 'bool'),
43 collectionById: fun('Get collection by specified id', [collectionParam], 'Option<NftDataStructsCollection>'),
44 collectionStats: fun('Get collection stats', [], 'NftDataStructsCollectionStats'),
45 allowed: fun('Check if user is allowed to use collection', [collectionParam, crossAccountParam()], 'bool'),
43 },46 },
44 types: {47 types: {
45 PalletCommonAccountBasicCrossAccountIdRepr: {48 PalletCommonAccountBasicCrossAccountIdRepr: {
64 constOnChainSchema: 'Vec<u8>',67 constOnChainSchema: 'Vec<u8>',
65 metaUpdatePermission: 'NftDataStructsMetaUpdatePermission',68 metaUpdatePermission: 'NftDataStructsMetaUpdatePermission',
66 },69 },
70 NftDataStructsCollectionStats: {
71 created: 'u32',
72 destroyed: 'u32',
73 alive: 'u32',
74 },
67 NftDataStructsCollectionId: 'u32',75 NftDataStructsCollectionId: 'u32',
68 NftDataStructsTokenId: 'u32',76 NftDataStructsTokenId: 'u32',
69 PalletNonfungibleItemData: mkDummy('NftItemData'),77 PalletNonfungibleItemData: mkDummy('NftItemData'),
modifiedtests/src/interfaces/nft/types.tsdiffbeforeafterboth
48 readonly dummyCollectionMode: u32;48 readonly dummyCollectionMode: u32;
49}49}
50
51/** @name NftDataStructsCollectionStats */
52export interface NftDataStructsCollectionStats extends Struct {
53 readonly created: u32;
54 readonly destroyed: u32;
55 readonly alive: u32;
56}
5057
51/** @name NftDataStructsCreateItemData */58/** @name NftDataStructsCreateItemData */
52export interface NftDataStructsCreateItemData extends Struct {59export interface NftDataStructsCreateItemData extends Struct {
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
39 'nonfungible',39 'nonfungible',
40 'refungible',40 'refungible',
41 'scheduler',41 'scheduler',
42 'nftpayment',
43 'charging',42 'charging',
44];43];
4544
modifiedtests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
8import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';
9import privateKey from './substrate/privateKey';9import privateKey from './substrate/privateKey';
10import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';10import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
11import {createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId} from './util/helpers';11import {createCollectionExpectSuccess, destroyCollectionExpectSuccess, getAdminList, normalizeAccountId, queryCollectionExpectSuccess} from './util/helpers';
1212
13chai.use(chaiAsPromised);13chai.use(chaiAsPromised);
14const expect = chai.expect;14const expect = chai.expect;
19 const collectionId = await createCollectionExpectSuccess();19 const collectionId = await createCollectionExpectSuccess();
20 const alice = privateKey('//Alice');20 const alice = privateKey('//Alice');
21 const bob = privateKey('//Bob');21 const bob = privateKey('//Bob');
22 const collection = (await api.query.common.collectionById(collectionId)).unwrap();22 const collection = await queryCollectionExpectSuccess(api, collectionId);
23 expect(collection.owner.toString()).to.be.deep.eq(alice.address);23 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
24 // first - add collection admin Bob24 // first - add collection admin Bob
25 const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));25 const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
43 const alice = privateKey('//Alice');43 const alice = privateKey('//Alice');
44 const bob = privateKey('//Bob');44 const bob = privateKey('//Bob');
45 const charlie = privateKey('//Charlie');45 const charlie = privateKey('//Charlie');
46 const collection = (await api.query.common.collectionById(collectionId)).unwrap();46 const collection = await queryCollectionExpectSuccess(api, collectionId);
47 expect(collection.owner.toString()).to.be.eq(alice.address);47 expect(collection.owner.toString()).to.be.eq(alice.address);
48 // first - add collection admin Bob48 // first - add collection admin Bob
49 const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));49 const addAdminTx = api.tx.nft.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
modifiedtests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth
18 removeCollectionSponsorExpectFailure,18 removeCollectionSponsorExpectFailure,
19 normalizeAccountId,19 normalizeAccountId,
20 addCollectionAdminExpectSuccess,20 addCollectionAdminExpectSuccess,
21 getCreatedCollectionCount,
21} from './util/helpers';22} from './util/helpers';
22import {Keyring} from '@polkadot/api';23import {Keyring} from '@polkadot/api';
23import {IKeyringPair} from '@polkadot/types/types';24import {IKeyringPair} from '@polkadot/types/types';
98 // Find the collection that never existed99 // Find the collection that never existed
99 let collectionId = 0;100 let collectionId = 0;
100 await usingApi(async (api) => {101 await usingApi(async (api) => {
101 collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;102 collectionId = await getCreatedCollectionCount(api) + 1;
102 });103 });
103104
104 await removeCollectionSponsorExpectFailure(collectionId);105 await removeCollectionSponsorExpectFailure(collectionId);
modifiedtests/src/removeFromAllowList.test.tsdiffbeforeafterboth
37 });37 });
3838
39 it('ensure bob is not in allowlist after removal', async () => {39 it('ensure bob is not in allowlist after removal', async () => {
40 await usingApi(async () => {40 await usingApi(async api => {
41 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});41 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
42 await enableAllowListExpectSuccess(alice, collectionId);42 await enableAllowListExpectSuccess(alice, collectionId);
43 await addToAllowListExpectSuccess(alice, collectionId, bob.address);43 await addToAllowListExpectSuccess(alice, collectionId, bob.address);
4444
45 await removeFromAllowListExpectSuccess(alice, collectionId, normalizeAccountId(bob.address));45 await removeFromAllowListExpectSuccess(alice, collectionId, normalizeAccountId(bob.address));
46 expect(await isAllowlisted(collectionId, bob.address)).to.be.false;46 expect(await isAllowlisted(api, collectionId, bob.address)).to.be.false;
47 });47 });
48 });48 });
4949
104 });104 });
105105
106 it('ensure address is not in allowlist after removal', async () => {106 it('ensure address is not in allowlist after removal', async () => {
107 await usingApi(async () => {107 await usingApi(async api => {
108 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});108 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
109 await enableAllowListExpectSuccess(alice, collectionId);109 await enableAllowListExpectSuccess(alice, collectionId);
110 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);110 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
111 await addToAllowListExpectSuccess(alice, collectionId, charlie.address);111 await addToAllowListExpectSuccess(alice, collectionId, charlie.address);
112 await removeFromAllowListExpectSuccess(bob, collectionId, normalizeAccountId(charlie.address));112 await removeFromAllowListExpectSuccess(bob, collectionId, normalizeAccountId(charlie.address));
113 expect(await isAllowlisted(collectionId, charlie.address)).to.be.false;113 expect(await isAllowlisted(api, collectionId, charlie.address)).to.be.false;
114 });114 });
115 });115 });
116116
modifiedtests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
11 destroyCollectionExpectSuccess,11 destroyCollectionExpectSuccess,
12 setCollectionSponsorExpectFailure,12 setCollectionSponsorExpectFailure,
13 addCollectionAdminExpectSuccess,13 addCollectionAdminExpectSuccess,
14 getCreatedCollectionCount,
14} from './util/helpers';15} from './util/helpers';
15import {Keyring} from '@polkadot/api';16import {Keyring} from '@polkadot/api';
16import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
77 // Find the collection that never existed78 // Find the collection that never existed
78 let collectionId = 0;79 let collectionId = 0;
79 await usingApi(async (api) => {80 await usingApi(async (api) => {
80 collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;81 collectionId = await getCreatedCollectionCount(api) + 1;
81 });82 });
8283
83 await setCollectionSponsorExpectFailure(collectionId, bob.address);84 await setCollectionSponsorExpectFailure(collectionId, bob.address);
modifiedtests/src/setConstOnChainSchema.test.tsdiffbeforeafterboth
12 createCollectionExpectSuccess,12 createCollectionExpectSuccess,
13 destroyCollectionExpectSuccess,13 destroyCollectionExpectSuccess,
14 addCollectionAdminExpectSuccess,14 addCollectionAdminExpectSuccess,
15 queryCollectionExpectSuccess,
16 getCreatedCollectionCount,
15} from './util/helpers';17} from './util/helpers';
1618
17chai.use(chaiAsPromised);19chai.use(chaiAsPromised);
37 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {39 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
38 await usingApi(async (api) => {40 await usingApi(async (api) => {
39 const collectionId = await createCollectionExpectSuccess();41 const collectionId = await createCollectionExpectSuccess();
40 const collection = (await api.query.common.collectionById(collectionId)).unwrap();42 const collection = await queryCollectionExpectSuccess(api, collectionId);
41 expect(collection.owner.toString()).to.be.eq(alice.address);43 expect(collection.owner.toString()).to.be.eq(alice.address);
42 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);44 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
43 await submitTransactionAsync(alice, setShema);45 await submitTransactionAsync(alice, setShema);
47 it('Collection admin can set the scheme', async () => {49 it('Collection admin can set the scheme', async () => {
48 await usingApi(async (api) => {50 await usingApi(async (api) => {
49 const collectionId = await createCollectionExpectSuccess();51 const collectionId = await createCollectionExpectSuccess();
50 const collection = (await api.query.common.collectionById(collectionId)).unwrap();52 const collection = await queryCollectionExpectSuccess(api, collectionId);
51 expect(collection.owner.toString()).to.be.eq(alice.address);53 expect(collection.owner.toString()).to.be.eq(alice.address);
52 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);54 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
53 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);55 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
60 const collectionId = await createCollectionExpectSuccess();62 const collectionId = await createCollectionExpectSuccess();
61 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);63 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
62 await submitTransactionAsync(alice, setShema);64 await submitTransactionAsync(alice, setShema);
63 const collection = (await api.query.common.collectionById(collectionId)).unwrap();65 const collection = await queryCollectionExpectSuccess(api, collectionId);
64 expect(collection.constOnChainSchema.toString()).to.be.eq(shema);66 expect(collection.constOnChainSchema.toString()).to.be.eq(shema);
65 });67 });
66 });68 });
71 it('Set a non-existent collection', async () => {73 it('Set a non-existent collection', async () => {
72 await usingApi(async (api) => {74 await usingApi(async (api) => {
73 // tslint:disable-next-line: radix75 // tslint:disable-next-line: radix
74 const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;76 const collectionId = await getCreatedCollectionCount(api) + 1;
75 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);77 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
76 await expect(submitTransactionExpectFailAsync(alice, setShema)).to.be.rejected;78 await expect(submitTransactionExpectFailAsync(alice, setShema)).to.be.rejected;
77 });79 });
97 it('Execute method not on behalf of the collection owner', async () => {99 it('Execute method not on behalf of the collection owner', async () => {
98 await usingApi(async (api) => {100 await usingApi(async (api) => {
99 const collectionId = await createCollectionExpectSuccess();101 const collectionId = await createCollectionExpectSuccess();
100 const collection = (await api.query.common.collectionById(collectionId)).unwrap();102 const collection = await queryCollectionExpectSuccess(api, collectionId);
101 expect(collection.owner.toString()).to.be.eq(alice.address);103 expect(collection.owner.toString()).to.be.eq(alice.address);
102 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);104 const setShema = api.tx.nft.setConstOnChainSchema(collectionId, shema);
103 await expect(submitTransactionExpectFailAsync(bob, setShema)).to.be.rejected;105 await expect(submitTransactionExpectFailAsync(bob, setShema)).to.be.rejected;
modifiedtests/src/setPublicAccessMode.test.tsdiffbeforeafterboth
19 enableAllowListExpectSuccess,19 enableAllowListExpectSuccess,
20 normalizeAccountId,20 normalizeAccountId,
21 addCollectionAdminExpectSuccess,21 addCollectionAdminExpectSuccess,
22 getCreatedCollectionCount,
22} from './util/helpers';23} from './util/helpers';
2324
24chai.use(chaiAsPromised);25chai.use(chaiAsPromised);
60 it('Set a non-existent collection', async () => {61 it('Set a non-existent collection', async () => {
61 await usingApi(async (api: ApiPromise) => {62 await usingApi(async (api: ApiPromise) => {
62 // tslint:disable-next-line: radix63 // tslint:disable-next-line: radix
63 const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;64 const collectionId = await getCreatedCollectionCount(api) + 1;
64 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'AllowList');65 const tx = api.tx.nft.setPublicAccessMode(collectionId, 'AllowList');
65 await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;66 await expect(submitTransactionExpectFailAsync(alice, tx)).to.be.rejected;
66 });67 });
modifiedtests/src/setVariableOnChainSchema.test.tsdiffbeforeafterboth
12 createCollectionExpectSuccess,12 createCollectionExpectSuccess,
13 destroyCollectionExpectSuccess,13 destroyCollectionExpectSuccess,
14 addCollectionAdminExpectSuccess,14 addCollectionAdminExpectSuccess,
15 queryCollectionExpectSuccess,
16 getCreatedCollectionCount,
15} from './util/helpers';17} from './util/helpers';
1618
17chai.use(chaiAsPromised);19chai.use(chaiAsPromised);
37 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {39 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
38 await usingApi(async (api) => {40 await usingApi(async (api) => {
39 const collectionId = await createCollectionExpectSuccess();41 const collectionId = await createCollectionExpectSuccess();
40 const collection = (await api.query.common.collectionById(collectionId)).unwrap();42 const collection = await queryCollectionExpectSuccess(api, collectionId);
41 expect(collection.owner.toString()).to.be.eq(alice.address);43 expect(collection.owner.toString()).to.be.eq(alice.address);
42 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);44 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
43 await submitTransactionAsync(alice, setSchema);45 await submitTransactionAsync(alice, setSchema);
49 const collectionId = await createCollectionExpectSuccess();51 const collectionId = await createCollectionExpectSuccess();
50 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);52 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
51 await submitTransactionAsync(alice, setSchema);53 await submitTransactionAsync(alice, setSchema);
52 const collection = (await api.query.common.collectionById(collectionId)).unwrap();54 const collection = await queryCollectionExpectSuccess(api, collectionId);
53 expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);55 expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);
5456
55 });57 });
61 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {63 it('Run extrinsic with parameters of the collection id, set the scheme', async () => {
62 await usingApi(async (api) => {64 await usingApi(async (api) => {
63 const collectionId = await createCollectionExpectSuccess();65 const collectionId = await createCollectionExpectSuccess();
64 const collection = (await api.query.common.collectionById(collectionId)).unwrap();66 const collection = await queryCollectionExpectSuccess(api, collectionId);
65 expect(collection.owner.toString()).to.be.eq(alice.address);67 expect(collection.owner.toString()).to.be.eq(alice.address);
66 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);68 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
67 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);69 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
75 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);77 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
76 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);78 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
77 await submitTransactionAsync(bob, setSchema);79 await submitTransactionAsync(bob, setSchema);
78 const collection = (await api.query.common.collectionById(collectionId)).unwrap();80 const collection = await queryCollectionExpectSuccess(api, collectionId);
79 expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);81 expect(collection.variableOnChainSchema.toString()).to.be.eq(schema);
8082
81 });83 });
87 it('Set a non-existent collection', async () => {89 it('Set a non-existent collection', async () => {
88 await usingApi(async (api) => {90 await usingApi(async (api) => {
89 // tslint:disable-next-line: radix91 // tslint:disable-next-line: radix
90 const collectionId = (await api.query.common.createdCollectionCount()).toNumber() + 1;92 const collectionId = await getCreatedCollectionCount(api) + 1;
91 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);93 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
92 await expect(submitTransactionExpectFailAsync(alice, setSchema)).to.be.rejected;94 await expect(submitTransactionExpectFailAsync(alice, setSchema)).to.be.rejected;
93 });95 });
113 it('Execute method not on behalf of the collection owner', async () => {115 it('Execute method not on behalf of the collection owner', async () => {
114 await usingApi(async (api) => {116 await usingApi(async (api) => {
115 const collectionId = await createCollectionExpectSuccess();117 const collectionId = await createCollectionExpectSuccess();
116 const collection = (await api.query.common.collectionById(collectionId)).unwrap();118 const collection = await queryCollectionExpectSuccess(api, collectionId);
117 expect(collection.owner.toString()).to.be.eq(alice.address);119 expect(collection.owner.toString()).to.be.eq(alice.address);
118 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);120 const setSchema = api.tx.nft.setVariableOnChainSchema(collectionId, schema);
119 await expect(submitTransactionExpectFailAsync(bob, setSchema)).to.be.rejected;121 await expect(submitTransactionExpectFailAsync(bob, setSchema)).to.be.rejected;
modifiedtests/src/transfer.test.tsdiffbeforeafterboth
19 transferExpectFailure,19 transferExpectFailure,
20 transferExpectSuccess,20 transferExpectSuccess,
21 addCollectionAdminExpectSuccess,21 addCollectionAdminExpectSuccess,
22 getCreatedCollectionCount,
23 toSubstrateAddress,
24 getTokenOwner,
25 normalizeAccountId,
26 getBalance as getTokenBalance,
22} from './util/helpers';27} from './util/helpers';
2328
24let alice: IKeyringPair;29let alice: IKeyringPair;
132 it('Transfer with not existed collection_id', async () => {137 it('Transfer with not existed collection_id', async () => {
133 await usingApi(async (api) => {138 await usingApi(async (api) => {
134 // nft139 // nft
135 const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();140 const nftCollectionCount = await getCreatedCollectionCount(api);
136 await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);141 await transferExpectFailure(nftCollectionCount + 1, 1, alice, bob, 1);
137 // fungible142 // fungible
138 const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();143 const fungibleCollectionCount = await getCreatedCollectionCount(api);
139 await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);144 await transferExpectFailure(fungibleCollectionCount + 1, 0, alice, bob, 1);
140 // reFungible145 // reFungible
141 const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();146 const reFungibleCollectionCount = await getCreatedCollectionCount(api);
142 await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);147 await transferExpectFailure(reFungibleCollectionCount + 1, 1, alice, bob, 1);
143 });148 });
144 });149 });
231 });236 });
232});237});
233238
239describe('Zero value transfer(From)', () => {
240 before(async () => {
241 await usingApi(async () => {
242 alice = privateKey('//Alice');
243 bob = privateKey('//Bob');
244 });
245 });
246
247 it('NFT', async () => {
248 await usingApi(async (api: ApiPromise) => {
249 const nftCollectionId = await createCollectionExpectSuccess();
250 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
251
252 const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);
253 await submitTransactionAsync(alice, transferTx);
254 const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));
255
256 expect(toSubstrateAddress(address)).to.be.equal(alice.address);
257 });
258 });
259
260 it('RFT', async () => {
261 await usingApi(async (api: ApiPromise) => {
262 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
263 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
264 const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);
265 const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);
266
267 const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);
268 await submitTransactionAsync(alice, transferTx);
269
270 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);
271 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);
272
273 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);
274 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);
275 });
276 });
277
278 it('Fungible', async () => {
279 await usingApi(async (api: ApiPromise) => {
280 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
281 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
282 const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);
283 const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);
284
285 const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);
286 await submitTransactionAsync(alice, transferTx);
287
288 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);
289 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);
290
291 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);
292 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);
293 });
294 });
295});
modifiedtests/src/transferFrom.test.tsdiffbeforeafterboth
19 transferFromExpectSuccess,19 transferFromExpectSuccess,
20 burnItemExpectSuccess,20 burnItemExpectSuccess,
21 setCollectionLimitsExpectSuccess,21 setCollectionLimitsExpectSuccess,
22 getCreatedCollectionCount,
22} from './util/helpers';23} from './util/helpers';
2324
24chai.use(chaiAsPromised);25chai.use(chaiAsPromised);
109 it('transferFrom for a collection that does not exist', async () => {110 it('transferFrom for a collection that does not exist', async () => {
110 await usingApi(async (api: ApiPromise) => {111 await usingApi(async (api: ApiPromise) => {
111 // nft112 // nft
112 const nftCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();113 const nftCollectionCount = await getCreatedCollectionCount(api);
113 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);114 await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);
114115
115 await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1);116 await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1);
116117
117 // fungible118 // fungible
118 const fungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();119 const fungibleCollectionCount = await getCreatedCollectionCount(api);
119 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);120 await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);
120121
121 await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1);122 await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1);
122 // reFungible123 // reFungible
123 const reFungibleCollectionCount = (await api.query.common.createdCollectionCount()).toNumber();124 const reFungibleCollectionCount = await getCreatedCollectionCount(api);
124 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);125 await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);
125126
126 await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1);127 await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1);
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
269 let collectionId = 0;269 let collectionId = 0;
270 await usingApi(async (api) => {270 await usingApi(async (api) => {
271 // Get number of collections before the transaction271 // Get number of collections before the transaction
272 const collectionCountBefore = (await api.query.common.createdCollectionCount()).toNumber();272 const collectionCountBefore = await getCreatedCollectionCount(api);
273273
274 // Run the CreateCollection transaction274 // Run the CreateCollection transaction
275 const alicePrivateKey = privateKey('//Alice');275 const alicePrivateKey = privateKey('//Alice');
288 const result = getCreateCollectionResult(events);288 const result = getCreateCollectionResult(events);
289289
290 // Get number of collections after the transaction290 // Get number of collections after the transaction
291 const collectionCountAfter = (await api.query.common.createdCollectionCount()).toNumber();291 const collectionCountAfter = await getCreatedCollectionCount(api);
292292
293 // Get the collection293 // Get the collection
294 const collection = (await api.query.common.collectionById(result.collectionId)).unwrap();294 const collection = await queryCollectionExpectSuccess(api, result.collectionId);
295295
296 // What to expect296 // What to expect
297 // tslint:disable-next-line:no-unused-expression297 // tslint:disable-next-line:no-unused-expression
325325
326 await usingApi(async (api) => {326 await usingApi(async (api) => {
327 // Get number of collections before the transaction327 // Get number of collections before the transaction
328 const collectionCountBefore = (await api.query.common.createdCollectionCount()).toNumber();328 const collectionCountBefore = await getCreatedCollectionCount(api);
329329
330 // Run the CreateCollection transaction330 // Run the CreateCollection transaction
331 const alicePrivateKey = privateKey('//Alice');331 const alicePrivateKey = privateKey('//Alice');
334 const result = getCreateCollectionResult(events);334 const result = getCreateCollectionResult(events);
335335
336 // Get number of collections after the transaction336 // Get number of collections after the transaction
337 const collectionCountAfter = (await api.query.common.createdCollectionCount()).toNumber();337 const collectionCountAfter = await getCreatedCollectionCount(api);
338338
339 // What to expect339 // What to expect
340 // tslint:disable-next-line:no-unused-expression340 // tslint:disable-next-line:no-unused-expression
364}364}
365365
366export async function findNotExistingCollection(api: ApiPromise): Promise<number> {366export async function findNotExistingCollection(api: ApiPromise): Promise<number> {
367 const totalNumber = (await api.query.common.createdCollectionCount()).toNumber();367 const totalNumber = await getCreatedCollectionCount(api);
368 const newCollection: number = totalNumber + 1;368 const newCollection: number = totalNumber + 1;
369 return newCollection;369 return newCollection;
370}370}
398 expect(result).to.be.true;398 expect(result).to.be.true;
399399
400 // What to expect400 // What to expect
401 expect((await api.query.common.collectionById(collectionId)).isNone).to.be.true;401 expect(await getDetailedCollectionInfo(api, collectionId)).to.be.null;
402 });402 });
403}403}
404404
432 const result = getGenericResult(events);432 const result = getGenericResult(events);
433433
434 // Get the collection434 // Get the collection
435 const collection = (await api.query.common.collectionById(collectionId)).unwrap();435 const collection = await queryCollectionExpectSuccess(api, collectionId);
436436
437 // What to expect437 // What to expect
438 expect(result.success).to.be.true;438 expect(result.success).to.be.true;
452 const result = getGenericResult(events);452 const result = getGenericResult(events);
453453
454 // Get the collection454 // Get the collection
455 const collection = (await api.query.common.collectionById(collectionId)).unwrap();455 const collection = await queryCollectionExpectSuccess(api, collectionId);
456456
457 // What to expect457 // What to expect
458 expect(result.success).to.be.true;458 expect(result.success).to.be.true;
490 const result = getGenericResult(events);490 const result = getGenericResult(events);
491491
492 // Get the collection492 // Get the collection
493 const collection = (await api.query.common.collectionById(collectionId)).unwrap();493 const collection = await queryCollectionExpectSuccess(api, collectionId);
494494
495 // What to expect495 // What to expect
496 expect(result.success).to.be.true;496 expect(result.success).to.be.true;
1058 const result = getGenericResult(events);1058 const result = getGenericResult(events);
10591059
1060 // Get the collection1060 // Get the collection
1061 const collection = (await api.query.common.collectionById(collectionId)).unwrap();1061 const collection = await queryCollectionExpectSuccess(api, collectionId);
10621062
1063 // What to expect1063 // What to expect
1064 // tslint:disable-next-line:no-unused-expression1064 // tslint:disable-next-line:no-unused-expression
1106 expect(result.success).to.be.true;1106 expect(result.success).to.be.true;
11071107
1108 // Get the collection1108 // Get the collection
1109 const collection = (await api.query.common.collectionById(collectionId)).unwrap();1109 const collection = await queryCollectionExpectSuccess(api, collectionId);
11101110
1111 expect(collection.mintMode.toHuman()).to.be.equal(enabled);1111 expect(collection.mintMode.toHuman()).to.be.equal(enabled);
1112 });1112 });
1138 });1138 });
1139}1139}
11401140
1141export async function isAllowlisted(collectionId: number, address: string | CrossAccountId) {1141export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId) {
1142 return await usingApi(async (api) => {1142 return (await api.rpc.nft.allowed(collectionId, normalizeAccountId(address))).toJSON();
1143 return (await api.query.common.allowlist(collectionId, normalizeAccountId(address))).toJSON();
1144 });
1145}1143}
11461144
1147export async function addToAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId | CrossAccountId) {1145export async function addToAllowListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId | CrossAccountId) {
1148 await usingApi(async (api) => {1146 await usingApi(async (api) => {
1149 expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.false;1147 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.false;
11501148
1151 // Run the transaction1149 // Run the transaction
1152 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));1150 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));
1153 const events = await submitTransactionAsync(sender, tx);1151 const events = await submitTransactionAsync(sender, tx);
1154 const result = getGenericResult(events);1152 const result = getGenericResult(events);
1155 expect(result.success).to.be.true;1153 expect(result.success).to.be.true;
11561154
1157 expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;1155 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
1158 });1156 });
1159}1157}
11601158
1161export async function addToAllowListAgainExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1159export async function addToAllowListAgainExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {
1162 await usingApi(async (api) => {1160 await usingApi(async (api) => {
11631161
1164 expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;1162 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
11651163
1166 // Run the transaction1164 // Run the transaction
1167 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));1165 const tx = api.tx.nft.addToAllowList(collectionId, normalizeAccountId(address));
1168 const events = await submitTransactionAsync(sender, tx);1166 const events = await submitTransactionAsync(sender, tx);
1169 const result = getGenericResult(events);1167 const result = getGenericResult(events);
1170 expect(result.success).to.be.true;1168 expect(result.success).to.be.true;
11711169
1172 expect(await isAllowlisted(collectionId, normalizeAccountId(address))).to.be.true;1170 expect(await isAllowlisted(api, collectionId, normalizeAccountId(address))).to.be.true;
1173 });1171 });
1174}1172}
11751173
12151213
1216export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1214export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)
1217 : Promise<NftDataStructsCollection | null> => {1215 : Promise<NftDataStructsCollection | null> => {
1218 return (await api.query.common.collectionById(collectionId)).unwrapOr(null);1216 return (await api.rpc.nft.collectionById(collectionId)).unwrapOr(null);
1219};1217};
12201218
1221export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1219export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {
1222 // set global object - collectionsCount1220 // set global object - collectionsCount
1223 return (await api.query.common.createdCollectionCount()).toNumber();1221 return (await api.rpc.nft.collectionStats()).created.toNumber();
1224};1222};
12251223
1226export async function queryCollectionExpectSuccess(api: ApiPromise, collectionId: number): Promise<NftDataStructsCollection> {1224export async function queryCollectionExpectSuccess(api: ApiPromise, collectionId: number): Promise<NftDataStructsCollection> {
1227 return (await api.query.common.collectionById(collectionId)).unwrap();1225 return (await api.rpc.nft.collectionById(collectionId)).unwrap();
1228}1226}
12291227
1230export async function waitNewBlocks(blocksCount = 1): Promise<void> {1228export async function waitNewBlocks(blocksCount = 1): Promise<void> {