git.delta.rocks / unique-network / refs/commits / 988b868fa538

difftreelog

Merge remote-tracking branch 'origin/develop' into feature/evm-coder-weights

Yaroslav Bolyukin2021-11-24parents: #3624bf8 #bf917ac.patch.diff
in: master

66 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]]
5825 "sp-io",5841 "sp-io",
5826 "sp-runtime",5842 "sp-runtime",
5827 "sp-std",5843 "sp-std",
5844 "up-evm-mapping",
5828 "up-sponsorship",5845 "up-sponsorship",
5829]5846]
58305847
6073 "sp-io",6090 "sp-io",
6074 "sp-runtime",6091 "sp-runtime",
6075 "sp-std",6092 "sp-std",
6093 "up-evm-mapping",
6076 "up-sponsorship",6094 "up-sponsorship",
6077]6095]
6078
6079[[package]]
6080name = "pallet-nft-transaction-payment"
6081version = "3.0.0"
6082dependencies = [
6083 "frame-benchmarking",
6084 "frame-support",
6085 "frame-system",
6086 "pallet-transaction-payment",
6087 "parity-scale-codec",
6088 "scale-info",
6089 "serde",
6090 "sp-core",
6091 "sp-io",
6092 "sp-runtime",
6093 "sp-std",
6094 "up-sponsorship",
6095]
60966096
6097[[package]]6097[[package]]
6098name = "pallet-nicks"6098name = "pallet-nicks"
11832source = "registry+https://github.com/rust-lang/crates.io-index"11832source = "registry+https://github.com/rust-lang/crates.io-index"
11833checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"11833checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
11834
11835[[package]]
11836name = "up-evm-mapping"
11837version = "0.1.0"
11838dependencies = [
11839 "frame-support",
11840 "sp-core",
11841]
1183411842
11835[[package]]11843[[package]]
11836name = "up-rpc"11844name = "up-rpc"
deletedDockerfilediffbeforeafterboth

no changes

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" && \
deletedDockerfile-unitdiffbeforeafterboth

no changes

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
deleteddocker-compose.ymldiffbeforeafterboth

no changes

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": [
deletednftTestnetSpec.jsondiffbeforeafterboth

no changes

deletednftTestnetSpecRaw.jsondiffbeforeafterboth

no changes

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
14 COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,14 COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
15 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,15 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
16 COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,16 COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,
17 WithdrawReasons,17 WithdrawReasons, CollectionStats,
18};18};
19pub use pallet::*;19pub use pallet::*;
20use sp_core::H160;20use sp_core::H160;
28#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]28#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
29pub struct CollectionHandle<T: Config> {29pub struct CollectionHandle<T: Config> {
30 pub id: CollectionId,30 pub id: CollectionId,
31 collection: Collection<T>,31 collection: Collection<T::AccountId>,
32 pub recorder: SubstrateRecorder<T>,32 pub recorder: SubstrateRecorder<T>,
33}33}
34impl<T: Config> WithRecorder<T> for CollectionHandle<T> {34impl<T: Config> WithRecorder<T> for CollectionHandle<T> {
82 }82 }
83}83}
84impl<T: Config> Deref for CollectionHandle<T> {84impl<T: Config> Deref for CollectionHandle<T> {
85 type Target = Collection<T>;85 type Target = Collection<T::AccountId>;
8686
87 fn deref(&self) -> &Self::Target {87 fn deref(&self) -> &Self::Target {
88 &self.collection88 &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
60 }60 }
6161
62 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {62 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
63 self.0.consume_sload()?;
63 Ok(<Pallet<T>>::allowed(contract_address, user, true))64 Ok(<Pallet<T>>::allowed(contract_address, user)
65 || !<AllowlistEnabled<T>>::get(contract_address))
64 }66 }
6567
66 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {68 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
109 value: sp_core::U256,111 value: sp_core::U256,
110 ) -> Option<PrecompileOutput> {112 ) -> Option<PrecompileOutput> {
111 // TODO: Extract to another OnMethodCall handler113 // TODO: Extract to another OnMethodCall handler
112 if !<Pallet<T>>::allowed(*target, *source, true) {114 if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {
113 return Some(PrecompileOutput {115 return Some(PrecompileOutput {
114 exit_status: ExitReason::Revert(ExitRevert::Reverted),116 exit_status: ExitReason::Revert(ExitRevert::Reverted),
115 cost: 0,117 cost: 0,
146pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);148pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
147impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {149impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
148 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {150 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
149 if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {151 if !<SelfSponsoring<T>>::get(&call.0) {
152 return None;
153 }
154 if !<Pallet<T>>::allowed(call.0, *who) {
155 return None;
156 }
150 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;157 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
158
151 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {159 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
152 let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);160 let limit = <SponsoringRateLimit<T>>::get(&call.0);
161
153 let limit_time = last_tx_block + rate_limit;162 let timeout = last_tx_block + limit.into();
154163 if block_number < timeout {
155 if block_number > limit_time {164 return None;
156 <SponsorBasket<T>>::insert(&call.0, who, block_number);165 }
157 return Some(call.0);
158 }
159 } else {166 }
167
160 <SponsorBasket<T>>::insert(&call.0, who, block_number);168 <SponsorBasket<T>>::insert(&call.0, who, block_number);
169
161 return Some(call.0);170 Some(call.0)
162 }
163 }
164 None
165 }171 }
166}172}
167173
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
17fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }17fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
18pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }18pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
19up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 19up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
20up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
2021
21[dependencies.codec]22[dependencies.codec]
22default-features = false23default-features = false
37 "pallet-ethereum/std",38 "pallet-ethereum/std",
38 "fp-evm/std",39 "fp-evm/std",
39 "up-sponsorship/std",40 "up-sponsorship/std",
41 "up-evm-mapping/std",
40]42]
4143
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
53 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;53 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;
5454
55 #[pallet::storage]55 #[pallet::storage]
56 pub(super) type Balance<T: Config> = StorageNMap<56 pub type Balance<T: Config> = StorageNMap<
57 Key = (57 Key = (
58 Key<Twox64Concat, CollectionId>,58 Key<Twox64Concat, CollectionId>,
59 Key<Blake2_128Concat, T::CrossAccountId>,59 Key<Blake2_128Concat, T::CrossAccountId>,
100}100}
101101
102impl<T: Config> Pallet<T> {102impl<T: Config> Pallet<T> {
103 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {103 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
104 PalletCommon::init_collection(data)104 <PalletCommon<T>>::init_collection(data)
105 }105 }
106 pub fn destroy_collection(106 pub fn destroy_collection(
107 collection: FungibleHandle<T>,107 collection: FungibleHandle<T>,
modifiedpallets/inflation/src/lib.rsdiffbeforeafterboth
8484
85 let block_interval: u32 = T::InflationBlockInterval::get().try_into().unwrap_or(0);85 let block_interval: u32 = T::InflationBlockInterval::get().try_into().unwrap_or(0);
86
87 // TODO: Rewrite inflation to use block timestamp instead of block number
88 // let _now = <timestamp::Module<T>>::get();
8689
87 // Recalculate inflation on the first block of the year (or if it is not initialized yet)90 // Recalculate inflation on the first block of the year (or if it is not initialized yet)
88 if (now % T::BlockNumber::from(YEAR)).is_zero() || <BlockInflation<T>>::get().is_zero() {91 if (now % T::BlockNumber::from(YEAR)).is_zero() || <BlockInflation<T>>::get().is_zero() {
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<T>>::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<T>>::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
162 0166 0
163 }167 }
164168
165 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.169 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.
166 ///170 ///
167 /// # Permissions171 /// # Permissions
168 ///172 ///
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
59 }59 }
6060
61 #[pallet::pallet]61 #[pallet::pallet]
62 #[pallet::generate_store(pub(super) trait Store)]62 #[pallet::generate_store(pub trait Store)]
63 pub struct Pallet<T>(_);63 pub struct Pallet<T>(_);
6464
65 #[pallet::storage]65 #[pallet::storage]
66 pub(super) type TokensMinted<T: Config> =66 pub type TokensMinted<T: Config> =
67 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;67 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
68 #[pallet::storage]68 #[pallet::storage]
69 pub(super) type TokensBurnt<T: Config> =69 pub type TokensBurnt<T: Config> =
70 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;70 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
7171
72 #[pallet::storage]72 #[pallet::storage]
73 pub(super) type TokenData<T: Config> = StorageNMap<73 pub type TokenData<T: Config> = StorageNMap<
74 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),74 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
75 Value = ItemData<T>,75 Value = ItemData<T>,
76 QueryKind = OptionQuery,76 QueryKind = OptionQuery,
77 >;77 >;
7878
79 /// Used to enumerate tokens owned by account79 /// Used to enumerate tokens owned by account
80 #[pallet::storage]80 #[pallet::storage]
81 pub(super) type Owned<T: Config> = StorageNMap<81 pub type Owned<T: Config> = StorageNMap<
82 Key = (82 Key = (
83 Key<Twox64Concat, CollectionId>,83 Key<Twox64Concat, CollectionId>,
84 Key<Blake2_128Concat, T::CrossAccountId>,84 Key<Blake2_128Concat, T::CrossAccountId>,
89 >;89 >;
9090
91 #[pallet::storage]91 #[pallet::storage]
92 pub(super) type AccountBalance<T: Config> = StorageNMap<92 pub type AccountBalance<T: Config> = StorageNMap<
93 Key = (93 Key = (
94 Key<Twox64Concat, CollectionId>,94 Key<Twox64Concat, CollectionId>,
95 Key<Blake2_128Concat, T::CrossAccountId>,95 Key<Blake2_128Concat, T::CrossAccountId>,
99 >;99 >;
100100
101 #[pallet::storage]101 #[pallet::storage]
102 pub(super) type Allowance<T: Config> = StorageNMap<102 pub type Allowance<T: Config> = StorageNMap<
103 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),103 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
104 Value = T::CrossAccountId,104 Value = T::CrossAccountId,
105 QueryKind = OptionQuery,105 QueryKind = OptionQuery,
142142
143// unchecked calls skips any permission checks143// unchecked calls skips any permission checks
144impl<T: Config> Pallet<T> {144impl<T: Config> Pallet<T> {
145 pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {145 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
146 PalletCommon::init_collection(data)146 <PalletCommon<T>>::init_collection(data)
147 }147 }
148 pub fn destroy_collection(148 pub fn destroy_collection(
149 collection: NonfungibleHandle<T>,149 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>,
modifiedpallets/scheduler/src/lib.rsdiffbeforeafterboth
62 traits::{Zero, One, BadOrigin, Saturating},62 traits::{Zero, One, BadOrigin, Saturating},
63};63};
64use frame_support::{64use frame_support::{
65 decl_module, decl_storage, decl_event, decl_error, IterableStorageMap,65 decl_module, decl_storage, decl_event, decl_error,
66 dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},66 dispatch::{Dispatchable, DispatchError, DispatchResult, Parameter},
67 traits::{67 traits::{
68 Get,68 Get,
456}456}
457457
458impl<T: Config> Module<T> {458impl<T: Config> Module<T> {
459 /// Migrate storage format from V1 to V2.
460 /// Return true if migration is performed.
461 pub fn migrate_v1_to_t2() -> bool {
462 if StorageVersion::get() == Releases::V1 {
463 StorageVersion::put(Releases::V2);
464
465 Agenda::<T>::translate::<
466 Vec<Option<ScheduledV1<<T as Config>::Call, T::BlockNumber>>>,
467 _,
468 >(|_, agenda| {
469 Some(
470 agenda
471 .into_iter()
472 .map(|schedule| {
473 schedule.map(|schedule| ScheduledV2 {
474 maybe_id: schedule.maybe_id,
475 priority: schedule.priority,
476 call: schedule.call,
477 maybe_periodic: schedule.maybe_periodic,
478 origin: system::RawOrigin::Root.into(),
479 _phantom: Default::default(),
480 })
481 })
482 .collect::<Vec<_>>(),
483 )
484 });
485
486 true
487 } else {
488 false
489 }
490 }
491
492 /// Helper to migrate scheduler when the pallet origin type has changed.
493 pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() {
494 Agenda::<T>::translate::<
495 Vec<Option<Scheduled<<T as Config>::Call, T::BlockNumber, OldOrigin, T::AccountId>>>,
496 _,
497 >(|_, agenda| {
498 Some(
499 agenda
500 .into_iter()
501 .map(|schedule| {
502 schedule.map(|schedule| Scheduled {
503 maybe_id: schedule.maybe_id,
504 priority: schedule.priority,
505 call: schedule.call,
506 maybe_periodic: schedule.maybe_periodic,
507 origin: schedule.origin.into(),
508 _phantom: Default::default(),
509 })
510 })
511 .collect::<Vec<_>>(),
512 )
513 });
514 }
515
516 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {459 fn resolve_time(when: DispatchTime<T::BlockNumber>) -> Result<T::BlockNumber, DispatchError> {
517 let now = frame_system::Pallet::<T>::block_number();460 let now = frame_system::Pallet::<T>::block_number();
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
deletedrun.shdiffbeforeafterboth

no changes

modifiedruntime/Cargo.tomldiffbeforeafterboth
71 'pallet-ethereum/std',71 'pallet-ethereum/std',
72 'fp-rpc/std',72 'fp-rpc/std',
73 'up-rpc/std',73 'up-rpc/std',
74 'up-evm-mapping/std',
74 'fp-self-contained/std',75 'fp-self-contained/std',
75 'parachain-info/std',76 'parachain-info/std',
76 'serde',77 'serde',
82 'pallet-nft/std',83 'pallet-nft/std',
83 'pallet-unq-scheduler/std',84 'pallet-unq-scheduler/std',
84 'pallet-nft-charge-transaction/std',85 'pallet-nft-charge-transaction/std',
85 'pallet-nft-transaction-payment/std',
86 'nft-data-structs/std',86 'nft-data-structs/std',
87 'sp-api/std',87 'sp-api/std',
88 'sp-block-builder/std',88 'sp-block-builder/std',
364default-features = false364default-features = false
365365
366[dependencies.orml-vesting]366[dependencies.orml-vesting]
367git = "https://github.com/open-web3-stack/open-runtime-module-library"367git = 'https://github.com/UniqueNetwork/open-runtime-module-library'
368version = "0.4.1-dev" 368version = "0.4.1-dev"
369default-features = false369default-features = false
370370
378derivative = "2.2.0"378derivative = "2.2.0"
379pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }379pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
380up-rpc = { path = "../primitives/rpc", default-features = false }380up-rpc = { path = "../primitives/rpc", default-features = false }
381up-evm-mapping = { path = "../primitives/evm-mapping", default-features = false }
381pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }382pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
382nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }383nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
383pallet-common = { default-features = false, path = "../pallets/common" }384pallet-common = { default-features = false, path = "../pallets/common" }
386pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }387pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }
387pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }388pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }
388# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }389# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
389pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
390pallet-nft-charge-transaction = {git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }390pallet-nft-charge-transaction = { git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }
391pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }391pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }
392pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }392pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }
deletedruntime/src/chain_extension.rsdiffbeforeafterboth

no changes

modifiedruntime/src/lib.rsdiffbeforeafterboth
761761
762impl pallet_common::Config for Runtime {762impl pallet_common::Config for Runtime {
763 type Event = Event;763 type Event = Event;
764 type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;764 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
765 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;765 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
766 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;766 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;
767767
796 type InflationBlockInterval = InflationBlockInterval;796 type InflationBlockInterval = InflationBlockInterval;
797}797}
798798
799parameter_types! {799// parameter_types! {
800 pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *800// pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *
801 RuntimeBlockWeights::get().max_block;801// RuntimeBlockWeights::get().max_block;
802 pub const MaxScheduledPerBlock: u32 = 50;802// pub const MaxScheduledPerBlock: u32 = 50;
803}803// }
804804
805pub struct Sponsoring;
806impl SponsoringResolve<AccountId, Call> for Sponsoring {
807 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>
808 where
809 Call: Dispatchable<Info = DispatchInfo>,805type EvmSponsorshipHandler = (
810 AccountId: AsRef<[u8]>,806 pallet_nft::NftEthSponsorshipHandler<Runtime>,
811 {
812 pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)807 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
813 }808);
814}
815
816type SponsorshipHandler = (809type SponsorshipHandler = (
817 pallet_nft::NftSponsorshipHandler<Runtime>,810 pallet_nft::NftSponsorshipHandler<Runtime>,
818 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,811 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
812 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
819);813);
814
815// impl pallet_unq_scheduler::Config for Runtime {
816// type Event = Event;
817// type Origin = Origin;
818// type PalletsOrigin = OriginCaller;
819// type Call = Call;
820// type MaximumWeight = MaximumSchedulerWeight;
821// type ScheduleOrigin = EnsureSigned<AccountId>;
822// type MaxScheduledPerBlock = MaxScheduledPerBlock;
823// type SponsorshipHandler = SponsorshipHandler;
824// type WeightInfo = ();
825// }
820826
821impl pallet_unq_scheduler::Config for Runtime {827impl pallet_evm_transaction_payment::Config for Runtime {
822 type Event = Event;828 type EvmSponsorshipHandler = EvmSponsorshipHandler;
823 type Origin = Origin;829 type Currency = Balances;
824 type PalletsOrigin = OriginCaller;
825 type Call = Call;
826 type MaximumWeight = MaximumSchedulerWeight;
827 type ScheduleOrigin = EnsureSigned<AccountId>;830 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
828 type MaxScheduledPerBlock = MaxScheduledPerBlock;831 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
829 type SponsorshipHandler = SponsorshipHandler;
830 type WeightInfo = ();
831}832}
832833
833impl pallet_nft_transaction_payment::Config for Runtime {834impl pallet_nft_charge_transaction::Config for Runtime {
834 type SponsorshipHandler = SponsorshipHandler;835 type SponsorshipHandler = SponsorshipHandler;
835}836}
836
837impl pallet_evm_transaction_payment::Config for Runtime {
838 type SponsorshipHandler = (
839 pallet_nft::NftEthSponsorshipHandler<Self>,
840 pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,
841 );
842 type Currency = Balances;
843}
844
845impl pallet_nft_charge_transaction::Config for Runtime {
846 type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;
847}
848837
849// impl pallet_contract_helpers::Config for Runtime {838// impl pallet_contract_helpers::Config for Runtime {
850// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;839// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;
894 // Unique Pallets883 // Unique Pallets
895 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,884 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
896 Nft: pallet_nft::{Pallet, Call, Storage} = 61,885 Nft: pallet_nft::{Pallet, Call, Storage} = 61,
897 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,886 // Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
898 NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,887 // free = 63
899 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,888 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,
900 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,889 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
901 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,890 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,
1081 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))1070 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))
1082 }1071 }
1083 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {1072 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {
1084 <pallet_nft::Pallet<Runtime>>::adminlist(collection)1073 <pallet_common::Pallet<Runtime>>::adminlist(collection)
1085 }1074 }
1086 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {1075 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {
1087 <pallet_nft::Pallet<Runtime>>::allowlist(collection)1076 <pallet_common::Pallet<Runtime>>::allowlist(collection)
1088 }1077 }
1078 fn allowed(collection: CollectionId, user: CrossAccountId) -> bool {
1079 <pallet_common::Pallet<Runtime>>::allowed(collection, user)
1080 }
1089 fn last_token_id(collection: CollectionId) -> TokenId {1081 fn last_token_id(collection: CollectionId) -> TokenId {
1090 dispatch_nft_runtime!(collection.last_token_id())1082 dispatch_nft_runtime!(collection.last_token_id())
1091 }1083 }
1084 fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>> {
1085 <pallet_common::CollectionById<Runtime>>::get(collection)
1086 }
1087 fn collection_stats() -> CollectionStats {
1088 <pallet_common::Pallet<Runtime>>::collection_stats()
1089 }
1092 }1090 }
10931091
1094 impl sp_api::Core<Block> for Runtime {1092 impl sp_api::Core<Block> for Runtime {
deletedsubkeydiffbeforeafterboth

no changes

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/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
136 });133 });
137134
138 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));
139 const matcherOwner = await createEthAccountWithBalance(api, web3);139 const matcherOwner = await createEthAccountWithBalance(api, web3);
140 const helpers = contractHelpers(web3, matcherOwner);
140 const escrow = await createEthAccountWithBalance(api, web3);141 const escrow = await createEthAccountWithBalance(api, web3);
142
141 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, {
142 from: matcherOwner,144 from: matcherOwner,
143 ...GAS_ARGS,145 ...GAS_ARGS,
144 });146 });
145 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});
146 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});148 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});
147 const helpers = contractHelpers(web3, matcherOwner);149
148 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});150 await transferBalanceToEth(api, alice, matcher.options.address);
149 await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});151 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
150152
151 const alice = privateKey('//Alice');
152 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});153 await transferBalanceToEth(api, alice, subToEth(alice.address));
153 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
154 await setCollectionSponsorExpectSuccess(collectionId, alice.address);154 await setCollectionSponsorExpectSuccess(collectionId, alice.address);
155 await confirmSponsorshipExpectSuccess(collectionId);155 await confirmSponsorshipExpectSuccess(collectionId);
156
157 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(alice.address), true).send({from: matcherOwner});
158 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(alice.address)));
159156
160 const seller = privateKey('//Bob');157 const seller = privateKey('//Seller/' + Date.now());
161 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});
162 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
163160
164 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);161 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
165162
166 // 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
167 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});164 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
168
169 // Token is owned by seller initially
170 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)});
171166
172 // 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(web3: Web3, api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {243export async function executeEthTxOnSub(web3: Web3, 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> {