difftreelog
merge develop
in: master
42 files changed
Cargo.lockdiffbeforeafterboth5097 "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]53095310[[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]530953245310[[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]557955955580[[package]]5596[[package]]5824 "sp-io",5840 "sp-io",5825 "sp-runtime",5841 "sp-runtime",5826 "sp-std",5842 "sp-std",5843 "up-evm-mapping",5827 "up-sponsorship",5844 "up-sponsorship",5828]5845]582958466072 "sp-io",6089 "sp-io",6073 "sp-runtime",6090 "sp-runtime",6074 "sp-std",6091 "sp-std",6092 "up-evm-mapping",6075 "up-sponsorship",6093 "up-sponsorship",6076]6094]60776078[[package]]6079name = "pallet-nft-transaction-payment"6080version = "3.0.0"6081dependencies = [6082 "frame-benchmarking",6083 "frame-support",6084 "frame-system",6085 "pallet-transaction-payment",6086 "parity-scale-codec",6087 "scale-info",6088 "serde",6089 "sp-core",6090 "sp-io",6091 "sp-runtime",6092 "sp-std",6093 "up-sponsorship",6094]609560956096[[package]]6096[[package]]6097name = "pallet-nicks"6097name = "pallet-nicks"11831source = "registry+https://github.com/rust-lang/crates.io-index"11831source = "registry+https://github.com/rust-lang/crates.io-index"11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"1183311834[[package]]11835name = "up-evm-mapping"11836version = "0.1.0"11837dependencies = [11838 "frame-support",11839 "sp-core",11840]118331184111834[[package]]11842[[package]]11835name = "up-rpc"11843name = "up-rpc"launch-config.jsondiffbeforeafterboth9 "rpcPort": 9843,9 "rpcPort": 9843,10 "port": 30444,10 "port": 30444,11 "flags": [11 "flags": [12 "--rpc-port=9843",13 "-lparachain::candidate_validation=debug"12 "-lparachain::candidate_validation=debug"14 ]13 ]15 },14 },19 "rpcPort": 9854,18 "rpcPort": 9854,20 "port": 30555,19 "port": 30555,21 "flags": [20 "flags": [22 "--rpc-port=9854",23 "-lparachain::candidate_validation=debug"21 "-lparachain::candidate_validation=debug"24 ]22 ]25 },23 },29 "rpcPort": 9865,27 "rpcPort": 9865,30 "port": 30666,28 "port": 30666,31 "flags": [29 "flags": [32 "--rpc-port=9865",33 "-lparachain::candidate_validation=debug"30 "-lparachain::candidate_validation=debug"34 ]31 ]35 },32 },39 "rpcPort": 9876,36 "rpcPort": 9876,40 "port": 30777,37 "port": 30777,41 "flags": [38 "flags": [42 "--rpc-port=9876",43 "-lparachain::candidate_validation=debug"39 "-lparachain::candidate_validation=debug"44 ]40 ]45 }41 }70 "name": "alice",66 "name": "alice",71 "flags": [67 "flags": [72 "--rpc-cors=all",68 "--rpc-cors=all",73 "--rpc-port=9933",74 "--unsafe-rpc-external",69 "--unsafe-rpc-external",75 "--unsafe-ws-external"70 "--unsafe-ws-external"76 ]71 ]82 "name": "bob",77 "name": "bob",83 "flags": [78 "flags": [84 "--rpc-cors=all",79 "--rpc-cors=all",85 "--rpc-port=9934",86 "--unsafe-rpc-external",80 "--unsafe-rpc-external",87 "--unsafe-ws-external"81 "--unsafe-ws-external"88 ]82 ]pallets/common/Cargo.tomldiffbeforeafterboth15sp-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]pallets/common/src/account.rsdiffbeforeafterboth2use 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;111112pub 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 + Default177 }177 }178}178}179180pub trait EvmBackwardsAddressMapping<AccountId> {181 fn from_account_id(account_id: AccountId) -> H160;182}183184/// Should have same mapping as EnsureAddressTruncated185pub 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}193179pallets/common/src/lib.rsdiffbeforeafterboth141pub 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>;154154155 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>;157157158 type Currency: Currency<Self::AccountId>;158 type Currency: Currency<Self::AccountId>;159 type CollectionCreationPrice: Get<159 type CollectionCreationPrice: Get<pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth595960 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {60 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {61 self.0.consume_sload()?;61 self.0.consume_sload()?;62 Ok(<Pallet<T>>::allowed(contract_address, user, true))62 Ok(<Pallet<T>>::allowed(contract_address, user)63 || !<AllowlistEnabled<T>>::get(contract_address))63 }64 }646565 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {66 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {113 value: sp_core::U256,114 value: sp_core::U256,114 ) -> Option<PrecompileOutput> {115 ) -> Option<PrecompileOutput> {115 // TODO: Extract to another OnMethodCall handler116 // TODO: Extract to another OnMethodCall handler116 if !<Pallet<T>>::allowed(*target, *source, true) {117 if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {117 return Some(PrecompileOutput {118 return Some(PrecompileOutput {118 exit_status: ExitReason::Revert(ExitRevert::Reverted),119 exit_status: ExitReason::Revert(ExitRevert::Reverted),119 cost: 0,120 cost: 0,151pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);152pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);152impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {153impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {153 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {154 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {154 if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {155 if !<SelfSponsoring<T>>::get(&call.0) {156 return None;157 }158 if !<Pallet<T>>::allowed(call.0, *who) {159 return None;160 }155 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;161 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;162156 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {163 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {157 let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);164 let limit = <SponsoringRateLimit<T>>::get(&call.0);165158 let limit_time = last_tx_block + rate_limit;166 let timeout = last_tx_block + limit.into();159167 if block_number < timeout {160 if block_number > limit_time {168 return None;161 <SponsorBasket<T>>::insert(&call.0, who, block_number);169 }162 return Some(call.0);163 }164 } else {170 }171165 <SponsorBasket<T>>::insert(&call.0, who, block_number);172 <SponsorBasket<T>>::insert(&call.0, who, block_number);173166 return Some(call.0);174 Some(call.0)167 }168 }169 None170 }175 }171}176}172177pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);77 }77 }787879 /// Default is returned if allowlist is disabled80 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) == user85 }81 }8682pallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 18up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }181919[dependencies.codec]20[dependencies.codec]20default-features = false21default-features = false35 "pallet-ethereum/std",36 "pallet-ethereum/std",36 "fp-evm/std",37 "fp-evm/std",37 "up-sponsorship/std",38 "up-sponsorship/std",39 "up-evm-mapping/std",38]40]3941pallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]223pub use pallet::*;45#[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;1314#[frame_support::pallet]15pub mod pallet {16 use super::*;1714 use sp_std::vec::Vec;18 use frame_support::traits::Currency;1516 type NegativeImbalanceOf<C, T> =17 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;19 use sp_std::vec::Vec;182019 #[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 }242825 #[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}3334type NegativeImbalanceOf<C, T> =35 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;283629 pub struct ChargeEvmLiquidityInfo<T>37pub struct ChargeEvmLiquidityInfo<T>30 where38where42 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 it44 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}104105/// 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>108where109 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::runner127 // 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}99141pallets/nft-transaction-payment/Cargo.tomldiffbeforeafterbothno changes
pallets/nft-transaction-payment/README.mddiffbeforeafterbothno changes
pallets/nft-transaction-payment/src/lib.rsdiffbeforeafterbothno changes
pallets/nft/Cargo.tomldiffbeforeafterboth34 '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" }136137137up-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 = [pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth1//! Implements EVM sponsoring logic via OnChargeEVMTransaction1//! Implements EVM sponsoring logic via OnChargeEVMTransaction223use 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;15use pallet_common::{12use up_evm_mapping::EvmBackwardsAddressMapping;16 CollectionById,13use pallet_evm::AddressMapping;17 account::{CrossAccountId, EvmBackwardsAddressMapping},18};191420use 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};2223struct AnyError;2425fn try_sponsor<T: Config>(26 caller: &H160,27 collection_id: CollectionId,28 collection: &Collection<T::AccountId>,29 call: &[u8],30) -> Result<(), AnyError> {31 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;32 match &collection.mode {33 crate::CollectionMode::NFT => {34 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)35 .map_err(|_| AnyError)?36 .ok_or(AnyError)?;37 match call {38 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {39 token_id,40 ..41 })42 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {43 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;44 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;45 let collection_limits = &collection.limits;46 let limit =47 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);4849 let mut sponsor = true;50 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {51 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);52 let limit_time = last_tx_block + limit.into();53 if block_number <= limit_time {54 sponsor = false;55 }56 }57 if sponsor {58 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);59 return Ok(());60 }61 }62 _ => {}63 }64 }65 crate::CollectionMode::Fungible(_) => {66 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)67 .map_err(|_| AnyError)?68 .ok_or(AnyError)?;69 #[allow(clippy::single_match)]70 match call {71 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {72 let who = T::CrossAccountId::from_eth(*caller);73 let collection_limits = &collection.limits;74 let limit = collection_limits75 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);7677 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}10317104pub 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 as pallet_common::Config>::EvmAddressMapping::into_account_id(*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 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {36 let token_id: TokenId = token_id.try_into().ok()?;112 if try_sponsor::<T>(who, collection_id, &collection, &call.1).is_ok() {37 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)113 return collection38 }114 .sponsorship39 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {115 .sponsor()40 let token_id: TokenId = token_id.try_into().ok()?;116 .cloned()41 withdraw_approve::<T>(&collection, &who, &token_id).map(|()| sponsor)117 .map(T::EvmBackwardsAddressMapping::from_account_id);42 }118 }43 _ => None,119 }44 }120 }45 }46 crate::CollectionMode::Fungible(_) => {47 let call = UniqueFungibleCall::parse(method_id, &mut reader).ok()??;48 #[allow(clippy::single_match)]49 match call {50 UniqueFungibleCall::ERC20(51 ERC20Call::Transfer { .. } | ERC20Call::TransferFrom { .. },52 ) => withdraw_transfer::<T>(&collection, &who, &TokenId::default())53 .map(|()| sponsor),54 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {55 withdraw_approve::<T>(&collection, &who, &TokenId::default())56 .map(|()| sponsor)57 }58 _ => None,59 }60 }121 None61 _ => None,62 }122 }63 }123}64}12465pallets/nft/src/lib.rsdiffbeforeafterboth136 //#region Tokens transfer rate limit baskets136 //#region Tokens transfer rate limit baskets137 /// (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 removed139 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 //#endregion147147148 /// Variable metadata sponsoring148 /// Variable metadata sponsoring149 /// 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 sponsoring152 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}153157249253250 <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);253257254 <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);255262256 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)?);594601595 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 }597612598 /// Destroys a concrete instance of NFT on behalf of the owner613 /// Destroys a concrete instance of NFT on behalf of the ownerpallets/nft/src/sponsorship.rsdiffbeforeafterboth1use 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, NFT_SPONSOR_TRANSFER_TIMEOUT,13 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,14 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,14};15};15use pallet_common::{CollectionById};16use pallet_common::{CollectionHandle};17use pallet_common::account::CrossAccountId;1819pub fn withdraw_transfer<T: Config>(20 collection: &CollectionHandle<T>,21 who: &T::AccountId,22 item_id: &TokenId,23) -> Option<()> {24 // preliminary sponsoring correctness check25 if !((pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner).as_sub() == who)26 || (pallet_refungible::Owned::<T>::get((27 collection.id,28 T::CrossAccountId::from_sub(who.clone()),29 item_id,30 ))) {31 return None;32 }3334 // sponsor timeout35 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;36 let limit = collection37 .limits38 .sponsor_transfer_timeout(match collection.mode {39 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,40 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,41 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,42 });4344 let last_tx_block = match collection.mode {45 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),46 CollectionMode::Fungible(_) => <FungibleTransferBasket<T>>::get(collection.id, who),47 CollectionMode::ReFungible => {48 <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who))49 }50 };5152 if let Some(last_tx_block) = last_tx_block {53 let timeout = last_tx_block + limit.into();54 if block_number < timeout {55 return None;56 }57 }5859 match collection.mode {60 CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),61 CollectionMode::Fungible(_) => {62 <FungibleTransferBasket<T>>::insert(collection.id, who, block_number)63 }64 CollectionMode::ReFungible => {65 <ReFungibleTransferBasket<T>>::insert((collection.id, item_id, who), block_number)66 }67 };6869 Some(())70}7172pub fn withdraw_create_item<T: Config>(73 collection: &CollectionHandle<T>,74 who: &T::AccountId,75 _properties: &CreateItemData,76) -> Option<()> {77 if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {78 return None;79 }8081 // sponsor timeout82 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;83 let limit = collection84 .limits85 .sponsor_transfer_timeout(match _properties {86 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,87 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,88 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,89 });9091 if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {92 let timeout = last_tx_block + limit.into();93 if block_number < timeout {94 return None;95 }96 }9798 CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);99100 Some(())101}102103pub fn withdraw_set_variable_meta_data<T: Config>(104 who: &T::AccountId,105 collection: &CollectionHandle<T>,106 item_id: &TokenId,107 data: &[u8],108) -> Option<()> {109 // preliminary sponsoring correctness check110 if !((pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner).as_sub() == who)111 || (pallet_refungible::Owned::<T>::get((112 collection.id,113 T::CrossAccountId::from_sub(who.clone()),114 item_id,115 ))) {116 return None;117 }118119 // Can't sponsor fungible collection, this tx will be rejected120 // as invalid121 if matches!(collection.mode, CollectionMode::Fungible(_)) {122 return None;123 }124 if data.len() > collection.limits.sponsored_data_size() as usize {125 return None;126 }127128 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;129 let limit = collection.limits.sponsored_data_rate_limit()?;130131 if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {132 let timeout = last_tx_block + limit.into();133 if block_number < timeout {134 return None;135 }136 }137138 <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);139140 Some(())141}142143pub fn withdraw_approve<T: Config>(144 collection: &CollectionHandle<T>,145 who: &T::AccountId,146 item_id: &TokenId,147) -> Option<()> {148 // sponsor timeout149 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;150 let limit = collection.limits.sponsor_approve_timeout();151152 let last_tx_block = match collection.mode {153 CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),154 CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),155 CollectionMode::ReFungible => {156 <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))157 }158 };159160 if let Some(last_tx_block) = last_tx_block {161 let timeout = last_tx_block + limit.into();162 if block_number < timeout {163 return None;164 }165 }166167 match collection.mode {168 CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),169 CollectionMode::Fungible(_) => {170 <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)171 }172 CollectionMode::ReFungible => {173 <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)174 }175 };176177 Some(())178}179180fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {181 let collection = CollectionHandle::new(id)?;182 let sponsor = collection.sponsorship.sponsor().cloned()?;183 Some((sponsor, collection))184}1618517pub struct NftSponsorshipHandler<T>(PhantomData<T>);186pub 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)?;2526 // sponsor timeout27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;2829 let limit = collection30 .limits31 .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);4445 // check free create limit46 if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {47 collection.sponsorship.sponsor().cloned()48 } else {49 None50 }51 }5253 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)?;5960 let mut sponsor_transfer = false;61 if collection.sponsorship.confirmed() {62 let collection_limits = collection.limits.clone();63 let collection_mode = collection.mode.clone();6465 // sponsor timeout66 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;67 sponsor_transfer = match collection_mode {68 CollectionMode::NFT => {69 // get correct limit70 let limit =71 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);7273 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 }8485 sponsored86 }87 CollectionMode::Fungible(_) => {88 // get correct limit89 let limit = collection_limits90 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);9192 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 }104105 sponsored106 }107 CollectionMode::ReFungible => {108 // get correct limit109 let limit = collection_limits110 .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);111112 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 }124125 sponsored126 }127 };128 }129130 if !sponsor_transfer {131 None132 } else {133 collection.sponsorship.sponsor().cloned()134 }135 }136137 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;143144 let collection = CollectionById::<T>::get(collection_id)?;145146 if collection.sponsorship.confirmed() &&147 // Can't sponsor fungible collection, this tx will be rejected148 // as invalid149 !matches!(collection.mode, CollectionMode::Fungible(_)) &&150 data.len() <= collection.limits.sponsored_data_size() as usize151 {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;154155 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 }164165 if !sponsor_metadata_changes {166 None167 } else {168 collection.sponsorship.sponsor().cloned()169 }170 }171}172173impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>187impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>174where188where181 collection_id,195 collection_id,182 data,196 data,183 ..197 ..184 } => Self::withdraw_create_item(who, collection_id, data),198 } => {199 let (sponsor, collection) = load(*collection_id)?;200 withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)201 }185 Call::transfer {202 Call::transfer {186 collection_id,203 collection_id,187 item_id,204 item_id,188 ..205 ..189 } => Self::withdraw_transfer(who, collection_id, item_id),206 }207 | Call::transfer_from {208 collection_id,209 item_id,210 ..211 } => {212 let (sponsor, collection) = load(*collection_id)?;213 withdraw_transfer::<T>(&collection, who, item_id).map(|()| sponsor)214 }215 Call::approve {216 collection_id,217 item_id,218 ..219 } => {220 let (sponsor, collection) = load(*collection_id)?;221 withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)222 }190 Call::set_variable_meta_data {223 Call::set_variable_meta_data {191 collection_id,224 collection_id,192 item_id,225 item_id,193 data,226 data,194 } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),227 } => {228 let (sponsor, collection) = load(*collection_id)?;229 withdraw_set_variable_meta_data::<T>(&who, &collection, item_id, data)230 .map(|()| sponsor)231 }195 _ => None,232 _ => None,196 }233 }197 }234 }pallets/nonfungible/src/lib.rsdiffbeforeafterboth58 }58 }595960 #[pallet::pallet]60 #[pallet::pallet]61 #[pallet::generate_store(pub(super) trait Store)]61 #[pallet::generate_store(pub trait Store)]62 pub struct Pallet<T>(_);62 pub struct Pallet<T>(_);636364 #[pallet::storage]64 #[pallet::storage]65 pub(super) type TokensMinted<T: Config> =65 pub type TokensMinted<T: Config> =66 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;66 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;67 #[pallet::storage]67 #[pallet::storage]68 pub(super) type TokensBurnt<T: Config> =68 pub type TokensBurnt<T: Config> =69 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;69 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;707071 #[pallet::storage]71 #[pallet::storage]72 pub(super) type TokenData<T: Config> = StorageNMap<72 pub type TokenData<T: Config> = StorageNMap<73 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),73 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),74 Value = ItemData<T>,74 Value = ItemData<T>,75 QueryKind = OptionQuery,75 QueryKind = OptionQuery,76 >;76 >;777778 /// Used to enumerate tokens owned by account78 /// Used to enumerate tokens owned by account79 #[pallet::storage]79 #[pallet::storage]80 pub(super) type Owned<T: Config> = StorageNMap<80 pub type Owned<T: Config> = StorageNMap<81 Key = (81 Key = (82 Key<Twox64Concat, CollectionId>,82 Key<Twox64Concat, CollectionId>,83 Key<Blake2_128Concat, T::CrossAccountId>,83 Key<Blake2_128Concat, T::CrossAccountId>,88 >;88 >;898990 #[pallet::storage]90 #[pallet::storage]91 pub(super) type AccountBalance<T: Config> = StorageNMap<91 pub type AccountBalance<T: Config> = StorageNMap<92 Key = (92 Key = (93 Key<Twox64Concat, CollectionId>,93 Key<Twox64Concat, CollectionId>,94 Key<Blake2_128Concat, T::CrossAccountId>,94 Key<Blake2_128Concat, T::CrossAccountId>,98 >;98 >;9999100 #[pallet::storage]100 #[pallet::storage]101 pub(super) type Allowance<T: Config> = StorageNMap<101 pub type Allowance<T: Config> = StorageNMap<102 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),102 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),103 Value = T::CrossAccountId,103 Value = T::CrossAccountId,104 QueryKind = OptionQuery,104 QueryKind = OptionQuery,328 .checked_add(data.len() as u32)328 .checked_add(data.len() as u32)329 .ok_or(ArithmeticError::Overflow)?;329 .ok_or(ArithmeticError::Overflow)?;330 ensure!(330 ensure!(331 tokens_minted < collection.limits.token_limit(),331 tokens_minted <= collection.limits.token_limit(),332 <CommonError<T>>::CollectionTokenLimitExceeded332 <CommonError<T>>::CollectionTokenLimitExceeded333 );333 );334 collection.consume_sstore()?;334 collection.consume_sstore()?;pallets/refungible/src/lib.rsdiffbeforeafterboth54 }54 }555556 #[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>(_);595960 #[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>;666667 #[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 >;737374 #[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 >;808081 /// Used to enumerate tokens owned by account81 /// Used to enumerate tokens owned by account82 #[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 >;929293 #[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 // Owner102 >;102 >;103103104 #[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 >;115115116 #[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>,primitives/evm-mapping/Cargo.tomldiffbeforeafterbothno changes
primitives/evm-mapping/src/lib.rsdiffbeforeafterbothno changes
primitives/nft/src/lib.rsdiffbeforeafterboth54pub 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;5657pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;565857// Schema limits59// Schema limits58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;246 pub variable_data: Vec<u8>,248 pub variable_data: Vec<u8>,247}249}248250251/// All fields are wrapped in `Option`s, where None means chain default249#[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 sponsored255 /// Some(v) - setVariableMetadata is sponsored258 /// Some(v) - setVariableMetadata is sponsored256 /// if there is v block between txs259 /// if there is v block between txs257 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>,259262260 // Timeouts for item types in passed blocks263 // Timeouts for item types in passed blocks261 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_timeout294 .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_limit308 .unwrap_or((None,))309 .0299 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))310 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))300 }311 }301}312}runtime/Cargo.tomldiffbeforeafterboth60 'pallet-transaction-payment/std',60 'pallet-transaction-payment/std',61 'pallet-transaction-payment-rpc-runtime-api/std',61 'pallet-transaction-payment-rpc-runtime-api/std',62 'pallet-treasury/std',62 'pallet-treasury/std',63 'pallet-vesting/std',63 # 'pallet-vesting/std',64 'pallet-evm/std',64 'pallet-evm/std',65 'pallet-evm-migration/std',65 'pallet-evm-migration/std',66 'pallet-evm-contract-helpers/std',66 'pallet-evm-contract-helpers/std',69 'pallet-ethereum/std',69 'pallet-ethereum/std',70 'fp-rpc/std',70 'fp-rpc/std',71 'up-rpc/std',71 'up-rpc/std',72 'up-evm-mapping/std',72 'fp-self-contained/std',73 'fp-self-contained/std',73 'parachain-info/std',74 'parachain-info/std',74 'serde',75 'serde',80 'pallet-nft/std',81 'pallet-nft/std',81 'pallet-unq-scheduler/std',82 'pallet-unq-scheduler/std',82 'pallet-nft-charge-transaction/std',83 'pallet-nft-charge-transaction/std',83 'pallet-nft-transaction-payment/std',84 'nft-data-structs/std',84 'nft-data-structs/std',85 'sp-api/std',85 'sp-api/std',86 'sp-block-builder/std',86 'sp-block-builder/std',98 'xcm-builder/std',98 'xcm-builder/std',99 'xcm-executor/std',99 'xcm-executor/std',100101 "orml-vesting/std",100]102]101limit-testing = ['pallet-nft/limit-testing', 'nft-data-structs/limit-testing']103limit-testing = ['pallet-nft/limit-testing', 'nft-data-structs/limit-testing']102104210git = 'https://github.com/paritytech/substrate.git'212git = 'https://github.com/paritytech/substrate.git'211branch = 'polkadot-v0.9.12'213branch = 'polkadot-v0.9.12'212214213[dependencies.pallet-vesting]215# [dependencies.pallet-vesting]214default-features = false216# default-features = false215git = 'https://github.com/paritytech/substrate.git'217# git = 'https://github.com/paritytech/substrate.git'216branch = 'polkadot-v0.9.12'218# branch = 'polkadot-v0.9.12'217219218[dependencies.sp-arithmetic]220[dependencies.sp-arithmetic]219default-features = false221default-features = false359branch = 'release-v0.9.12'361branch = 'release-v0.9.12'360default-features = false362default-features = false361363364[dependencies.orml-vesting]365git = 'https://github.com/UniqueNetwork/open-runtime-module-library'366version = "0.4.1-dev" 367default-features = false362368363################################################################################369################################################################################364# local dependencies370# local dependencies370derivative = "2.2.0"376derivative = "2.2.0"371pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }377pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }372up-rpc = { path = "../primitives/rpc", default-features = false }378up-rpc = { path = "../primitives/rpc", default-features = false }379up-evm-mapping = { path = "../primitives/evm-mapping", default-features = false }373pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }380pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }374nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }381nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }375pallet-common = { default-features = false, path = "../pallets/common" }382pallet-common = { default-features = false, path = "../pallets/common" }378pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }385pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }379pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }386pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }380# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }387# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }381pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }382pallet-nft-charge-transaction = {git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }388pallet-nft-charge-transaction = { git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }383pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }389pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }384pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }390pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }runtime/src/lib.rsdiffbeforeafterboth22use sp_runtime::{22use sp_runtime::{23 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,23 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,24 traits::{24 traits::{25 AccountIdLookup, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,25 AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,26 AccountIdConversion,27 },26 },28 transaction_validity::{TransactionSource, TransactionValidity},27 transaction_validity::{TransactionSource, TransactionValidity},70use fp_rpc::TransactionStatus;69use fp_rpc::TransactionStatus;71use sp_core::crypto::Public;70use sp_core::crypto::Public;72use sp_runtime::{71use sp_runtime::{73 traits::{Dispatchable, PostDispatchInfoOf},72 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},74 transaction_validity::TransactionValidityError,73 transaction_validity::TransactionValidityError,75};74};7675515 type Call = Call;514 type Call = Call;516}515}516517pub struct RelayChainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);518519impl<T: cumulus_pallet_parachain_system::Config> BlockNumberProvider520 for RelayChainBlockNumberProvider<T>521{522 type BlockNumber = BlockNumber;523524 fn current_block_number() -> Self::BlockNumber {525 cumulus_pallet_parachain_system::Pallet::<T>::validation_data()526 .map(|d| d.relay_parent_number)527 .unwrap_or_default()528 }529}517530518parameter_types! {531parameter_types! {519 pub const MinVestedTransfer: Balance = 10 * UNIQUE;532 pub const MinVestedTransfer: Balance = 10 * UNIQUE;533 pub const MaxVestingSchedules: u32 = 28;520}534}521535522impl pallet_vesting::Config for Runtime {536impl orml_vesting::Config for Runtime {523 type Event = Event;537 type Event = Event;524 type Currency = Balances;538 type Currency = pallet_balances::Pallet<Runtime>;525 type BlockNumberToBalance = ConvertInto;539 type MinVestedTransfer = MinVestedTransfer;526 type MinVestedTransfer = MinVestedTransfer;540 type VestedTransferOrigin = EnsureSigned<AccountId>;527 type WeightInfo = ();541 type WeightInfo = ();528 const MAX_VESTING_SCHEDULES: u32 = 28;542 type MaxVestingSchedules = MaxVestingSchedules;543 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;529}544}530545531parameter_types! {546parameter_types! {721736722impl pallet_common::Config for Runtime {737impl pallet_common::Config for Runtime {723 type Event = Event;738 type Event = Event;724 type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;739 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;725 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;726 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;727742762 pub const MaxScheduledPerBlock: u32 = 50;777 pub const MaxScheduledPerBlock: u32 = 50;763}778}764779765pub struct Sponsoring;766impl SponsoringResolve<AccountId, Call> for Sponsoring {767 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>768 where769 Call: Dispatchable<Info = DispatchInfo>,780type EvmSponsorshipHandler = (770 AccountId: AsRef<[u8]>,781 pallet_nft::NftEthSponsorshipHandler<Runtime>,771 {772 pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)782 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,773 }783);774}775776type SponsorshipHandler = (784type SponsorshipHandler = (777 pallet_nft::NftSponsorshipHandler<Runtime>,785 pallet_nft::NftSponsorshipHandler<Runtime>,778 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,786 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,787 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,779);788);780789781impl pallet_unq_scheduler::Config for Runtime {790impl pallet_unq_scheduler::Config for Runtime {790 type WeightInfo = ();799 type WeightInfo = ();791}800}792793impl pallet_nft_transaction_payment::Config for Runtime {794 type SponsorshipHandler = SponsorshipHandler;795}796801797impl pallet_evm_transaction_payment::Config for Runtime {802impl pallet_evm_transaction_payment::Config for Runtime {803 type EvmSponsorshipHandler = EvmSponsorshipHandler;804 type Currency = Balances;798 type SponsorshipHandler = (805 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;799 pallet_nft::NftEthSponsorshipHandler<Self>,800 pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,801 );802 type Currency = Balances;806 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;803}807}804808805impl pallet_nft_charge_transaction::Config for Runtime {809impl pallet_nft_charge_transaction::Config for Runtime {806 type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;810 type SponsorshipHandler = SponsorshipHandler;807}811}808812809// impl pallet_contract_helpers::Config for Runtime {813// impl pallet_contract_helpers::Config for Runtime {841 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,845 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,842 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,846 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,843 System: system::{Pallet, Call, Storage, Config, Event<T>} = 36,847 System: system::{Pallet, Call, Storage, Config, Event<T>} = 36,844 Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,848 Vesting: orml_vesting::{Pallet, Storage, Call, Event<T>, Config<T>} = 37,849 // Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,845 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,850 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,846851847 // XCM helpers.852 // XCM helpers.854 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,859 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,855 Nft: pallet_nft::{Pallet, Call, Storage} = 61,860 Nft: pallet_nft::{Pallet, Call, Storage} = 61,856 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,861 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,857 NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,862 // free = 63858 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,863 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,859 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,864 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,860 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,865 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,tests/src/approve.test.tsdiffbeforeafterboth19 addCollectionAdminExpectSuccess,19 addCollectionAdminExpectSuccess,20 adminApproveFromExpectSuccess,20 adminApproveFromExpectSuccess,21 getCreatedCollectionCount,21 getCreatedCollectionCount,22 transferFromExpectSuccess,23 transferFromExpectFail,22} from './util/helpers';24} from './util/helpers';232524chai.use(chaiAsPromised);26chai.use(chaiAsPromised);27const expect = chai.expect;252826describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {29describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {27 let alice: IKeyringPair;30 let alice: IKeyringPair;79 });82 });80});83});8485describe('Normal user can approve other users to transfer:', () => {86 let alice: IKeyringPair;87 let bob: IKeyringPair;88 let charlie: IKeyringPair;8990 before(async () => {91 await usingApi(async () => {92 alice = privateKey('//Alice');93 bob = privateKey('//Bob');94 charlie = privateKey('//Charlie');95 });96 }); 9798 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 });103104 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 });109110 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});116117describe('Approved users can transferFrom up to approved amount:', () => {118 let alice: IKeyringPair;119 let bob: IKeyringPair;120 let charlie: IKeyringPair;121122 before(async () => {123 await usingApi(async () => {124 alice = privateKey('//Alice');125 bob = privateKey('//Bob');126 charlie = privateKey('//Charlie');127 });128 }); 129130 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 });136137 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 });143144 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});151152describe('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;156157 before(async () => {158 await usingApi(async () => {159 alice = privateKey('//Alice');160 bob = privateKey('//Bob');161 charlie = privateKey('//Charlie');162 });163 }); 164165 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 });172173 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 });180181 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});189190describe('Approved amount decreases by the transferred amount.:', () => {191 let alice: IKeyringPair;192 let bob: IKeyringPair;193 let charlie: IKeyringPair;194 let dave: IKeyringPair;195196 before(async () => {197 await usingApi(async () => {198 alice = privateKey('//Alice');199 bob = privateKey('//Bob');200 charlie = privateKey('//Charlie');201 dave = privateKey('//Dave');202 });203 }); 204205 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});213214describe('User may clear the approvals to approving for 0 amount:', () => {215 let alice: IKeyringPair;216 let bob: IKeyringPair;217 let charlie: IKeyringPair;218219 before(async () => {220 await usingApi(async () => {221 alice = privateKey('//Alice');222 bob = privateKey('//Bob');223 charlie = privateKey('//Charlie');224 });225 });226227 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 });234235 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 });242243 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});252253describe('User cannot approve for the amount greater than they own:', () => {254 let alice: IKeyringPair;255 let bob: IKeyringPair;256 let charlie: IKeyringPair;257258 before(async () => {259 await usingApi(async () => {260 alice = privateKey('//Alice');261 bob = privateKey('//Bob');262 charlie = privateKey('//Charlie');263 });264 });265266 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 });271272 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 });277278 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});284285describe('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;290291 before(async () => {292 await usingApi(async () => {293 alice = privateKey('//Alice');294 bob = privateKey('//Bob');295 charlie = privateKey('//Charlie');296 dave = privateKey('//Dave');297 });298 }); 299300 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 });307308 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 });315316 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});324325describe('Repeated approvals add up', () => {326 let alice: IKeyringPair;327 let bob: IKeyringPair;328 let charlie: IKeyringPair;329 let dave: IKeyringPair;330331 before(async () => {332 await usingApi(async () => {333 alice = privateKey('//Alice');334 bob = privateKey('//Bob');335 charlie = privateKey('//Charlie');336 dave = privateKey('//Dave');337 });338 }); 339340 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 });349350 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 });359360 // Canceled by changing approve logic361 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 });367368 // Canceled by changing approve logic369 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});376377describe('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;381382 before(async () => {383 await usingApi(async () => {384 alice = privateKey('//Alice');385 bob = privateKey('//Bob');386 charlie = privateKey('//Charlie');387 });388 });389390 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);393394 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);395 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);396 });397});8139882describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {399describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {83 let alice: IKeyringPair;400 let alice: IKeyringPair;173 });490 });174});491});175176describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {177 let alice: IKeyringPair;178 let bob: IKeyringPair;179 let charlie: IKeyringPair;180181 before(async () => {182 await usingApi(async () => {183 alice = privateKey('//Alice');184 bob = privateKey('//Bob');185 charlie = privateKey('//Charlie');186 });187 });188189 it('can be called by collection admin on non-owned item', async () => {190 const collectionId = await createCollectionExpectSuccess();191 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);192193 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);194 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);195 });196});197492tests/src/confirmSponsorship.test.tsdiffbeforeafterboth252 const zeroBalance = await findUnusedAddress(api);252 const zeroBalance = await findUnusedAddress(api);253253254 // Mint token for alice254 // Mint token for alice255 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);255 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);256256257 // Transfer this token from Alice to unused address and back258 // Alice to Zero gets sponsored259 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);258259 // Zero to alice gets sponsored260 const events1 = await submitTransactionAsync(alice, aliceToZero);260 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);261 const result1 = getGenericResult(events1);261 const result1 = getGenericResult(events1);262 expect(result1.success).to.be.true;262 expect(result1.success).to.be.true;263263264 // Second transfer should fail264 // Second transfer should fail265 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();266 const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);267 const badTransaction = async function () {268 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);269 };270 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');271 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);272269273 // Try again after Zero gets some balance - now it should succeed270 // Try again after Zero gets some balance - now it should succeed274 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);271 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);277 const result2 = getGenericResult(events2);274 const result2 = getGenericResult(events2);278 expect(result2.success).to.be.true;275 expect(result2.success).to.be.true;279280 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);281 });276 });282 });277 });283278tests/src/eth/marketplace/MarketPlace.abidiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlace.bindiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlace.soldiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceKSM.abidiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceKSM.bindiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceKSM.soldiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceUNQ.abidiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceUNQ.bindiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceUNQ.soldiffbeforeafterbothno changes
tests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth1import {readFile} from 'fs/promises';1import {readFile} from 'fs/promises';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 {createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';4import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';5import {collectionIdToAddress, 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';121213describe('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);2016 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.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}/MarketPlaceUNQ.bin`)).toString()}).send({from: matcherOwner});25 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});212622 const alice = privateKey('//Alice');23 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});27 await transferBalanceToEth(api, alice, matcher.options.address);24 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});28 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});252926 const seller = privateKey('//Bob');30 await transferBalanceToEth(api, alice, subToEth(alice.address));2728 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);31 await setCollectionSponsorExpectSuccess(collectionId, alice.address);2932 await confirmSponsorshipExpectSuccess(collectionId);30 // To transfer item to matcher it first needs to be transfered to EVM account of bob3334 const seller = privateKey('//Seller/' + Date.now());31 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});35 await addToAllowListExpectSuccess(alice, collectionId, {Ethereum:subToEth(seller.address)});32 // Fees will be paid from EVM account, so we should have some balance here33 await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);36 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});3738 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);3940 // To transfer item to matcher it first needs to be transfered to EVM account of seller34 await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);41 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});3536 // Token is owned by seller initially37 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)});384339 // Ask44 // Ask40 {45 {41 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));46 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));42 await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, '0x0000000000000000000000000000000000000000', evmCollection.options.address, tokenId, 1));47 await executeEthTxOnSub(api, seller, matcher, m => m.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, tokenId));43 }48 }444945 // Token is transferred to matcher50 // Token is transferred to matcher48 // Buy53 // Buy49 {54 {50 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);55 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);51 // There is two functions named 'buy', so we should provide full signature52 await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), {value: PRICE});56 await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId), {value: PRICE});53 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);57 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);54 }58 }555964 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Substrate: alice.address});67 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Substrate: alice.address});65 });68 });666970 // selling for custom tokens excluded from release67 itWeb3('With custom ERC20', async ({api, web3}) => {71 itWeb3.skip('With custom ERC20', async ({api, web3}) => {68 const matcherOwner = await createEthAccountWithBalance(api, web3);72 const matcherOwner = await createEthAccountWithBalance(api, web3);69 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {73 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {70 from: matcherOwner,74 from: matcherOwner,71 ...GAS_ARGS,75 ...GAS_ARGS,72 });76 });73 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString()}).send({from: matcherOwner});77 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});747875 const alice = privateKey('//Alice');79 const alice = privateKey('//Alice');76 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});80 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});129 });133 });130134131 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));132 const matcherOwner = await createEthAccountWithBalance(api, web3);139 const matcherOwner = await createEthAccountWithBalance(api, web3);140 const helpers = contractHelpers(web3, matcherOwner);133 const escrow = await createEthAccountWithBalance(api, web3);141 const escrow = await createEthAccountWithBalance(api, web3);142134 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceKSM.abi`)).toString()), undefined, {143 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {135 from: matcherOwner,144 from: matcherOwner,136 ...GAS_ARGS,145 ...GAS_ARGS,137 });146 });138 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlaceKSM.bin`)).toString(), arguments: [escrow]}).send({from: matcherOwner});147 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});139140 const ksmToken = 11;141142 const alice = privateKey('//Alice');148 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});149143 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});150 await transferBalanceToEth(api, alice, matcher.options.address);144 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});151 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});152153 await transferBalanceToEth(api, alice, subToEth(alice.address));154 await setCollectionSponsorExpectSuccess(collectionId, alice.address);155 await confirmSponsorshipExpectSuccess(collectionId);145156146 const seller = privateKey('//Bob');157 const seller = privateKey('//Seller/' + Date.now());147148 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);158 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});149150 // To transfer item to matcher it first needs to be transfered to EVM account of bob151 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));152 // Fees will be paid from EVM account, so we should have some balance here160153 await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);161 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);162163 // To transfer item to matcher it first needs to be transfered to EVM account of seller154 await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);164 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});155156 // Token is owned by seller initially157 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)});158166159 // Ask167 // Ask160 {168 {161 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));169 await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));162 await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, ksmToken, evmCollection.options.address, tokenId, 1));170 await executeEthTxOnSub(api, seller, matcher, m => m.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, tokenId));163 }171 }164172165 // Token is transferred to matcher173 // Token is transferred to matcher166 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});174 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});167175168 // Give buyer KSM176 // Give buyer KSM169 await matcher.methods.deposit(PRICE, ksmToken, subToEth(alice.address)).send({from: escrow});177 await matcher.methods.depositKSM(PRICE, subToEth(alice.address)).send({from: escrow});170178171 // Buy179 // Buy172 {180 {173 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal('0');181 expect(await matcher.methods.balanceKSM(subToEth(seller.address)).call()).to.be.equal('0');174 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal(PRICE.toString());182 expect(await matcher.methods.balanceKSM(subToEth(alice.address)).call()).to.be.equal(PRICE.toString());175183176 await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId));184 await executeEthTxOnSub(api, alice, matcher, m => m.buyKSM(evmCollection.options.address, tokenId, subToEth(alice.address), subToEth(alice.address)));177185178 // Price is removed from buyer balance, and added to seller186 // Price is removed from buyer balance, and added to seller179 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal('0');187 expect(await matcher.methods.balanceKSM(subToEth(alice.address)).call()).to.be.equal('0');180 expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal(PRICE.toString());188 expect(await matcher.methods.balanceKSM(subToEth(seller.address)).call()).to.be.equal(PRICE.toString());181 }189 }182190183 // Token is transferred to evm account of alice191 // Token is transferred to evm account of alicetests/src/eth/util/helpers.tsdiffbeforeafterboth228 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}230230231/**232 * Execute ethereum method call using substrate account233 * @param to target contract234 * @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 * # Example239 * ```ts240 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));241 * ```242 */231export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {243export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {232 const tx = api.tx.evm.call(244 const tx = api.tx.evm.call(233 subToEth(from.address),245 subToEth(from.address),246 return (await getBalance(api, [evmToAddress(address)]))[0];258 return (await getBalance(api, [evmToAddress(address)]))[0];247}259}248260261/**262 * Measure how much gas given closure consumes263 * 264 * @param user which user balance will be checked265 */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);251268tests/src/limits.test.tsdiffbeforeafterboth78 });78 });79});79});808081describe('Sponsor timeout (NFT)', () => {81describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {82 let alice: IKeyringPair;82 let alice: IKeyringPair;83 let bob: IKeyringPair;83 let bob: IKeyringPair;84 let charlie: IKeyringPair;84 let charlie: IKeyringPair;91 });91 });92 });92 });939394 it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {94 it.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {95 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});95 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});96 await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});96 await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});97 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');97 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');141 });141 });142});142});143143144describe('Sponsor timeout (Fungible)', () => {144describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {145 let alice: IKeyringPair;145 let alice: IKeyringPair;146 let bob: IKeyringPair;146 let bob: IKeyringPair;147 let charlie: IKeyringPair;147 let charlie: IKeyringPair;208 });208 });209});209});210210211describe('Sponsor timeout (ReFungible)', () => {211describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {212 let alice: IKeyringPair;212 let alice: IKeyringPair;213 let bob: IKeyringPair;213 let bob: IKeyringPair;214 let charlie: IKeyringPair;214 let charlie: IKeyringPair;tests/src/overflow.test.tsdiffbeforeafterboth13chai.use(chaiAsPromised);13chai.use(chaiAsPromised);14const expect = chai.expect;14const expect = chai.expect;151516describe('Integration Test fungible overflows', () => {16describe.skip('Integration Test fungible overflows', () => {17 let alice: IKeyringPair;17 let alice: IKeyringPair;18 let bob: IKeyringPair;18 let bob: IKeyringPair;19 let charlie: IKeyringPair;19 let charlie: IKeyringPair;tests/src/pallet-presence.test.tsdiffbeforeafterboth39 'nonfungible',39 'nonfungible',40 'refungible',40 'refungible',41 'scheduler',41 'scheduler',42 'nftpayment',43 'charging',42 'charging',44];43];4544tests/src/setOffchainSchema.test.tsdiffbeforeafterboth40 await setOffchainSchemaExpectSuccess(alice, collectionId, DATA);40 await setOffchainSchemaExpectSuccess(alice, collectionId, DATA);41 const collection = await queryCollectionExpectSuccess(api, collectionId);41 const collection = await queryCollectionExpectSuccess(api, collectionId);424243 expect(collection.offchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));43 expect('0x' + Buffer.from(collection.offchainSchema).toString('hex')).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));44 });44 });45 });45 });464651 await setOffchainSchemaExpectSuccess(bob, collectionId, DATA);51 await setOffchainSchemaExpectSuccess(bob, collectionId, DATA);52 const collection = await queryCollectionExpectSuccess(api, collectionId);52 const collection = await queryCollectionExpectSuccess(api, collectionId);535354 expect(collection.offchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));54 expect('0x' + Buffer.from(collection.offchainSchema).toString('hex')).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));55 });55 });56 });56 });57});57});tests/src/transfer.test.tsdiffbeforeafterboth20 transferExpectSuccess,20 transferExpectSuccess,21 addCollectionAdminExpectSuccess,21 addCollectionAdminExpectSuccess,22 getCreatedCollectionCount,22 getCreatedCollectionCount,23 toSubstrateAddress,24 getTokenOwner,25 normalizeAccountId,26 getBalance as getTokenBalance,23} from './util/helpers';27} from './util/helpers';242825let alice: IKeyringPair;29let alice: IKeyringPair;232 });236 });233});237});234238239describe('Zero value transfer(From)', () => {240 before(async () => {241 await usingApi(async () => {242 alice = privateKey('//Alice');243 bob = privateKey('//Bob');244 });245 });246247 it('NFT', async () => {248 await usingApi(async (api: ApiPromise) => {249 const nftCollectionId = await createCollectionExpectSuccess();250 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');251252 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));255256 expect(toSubstrateAddress(address)).to.be.equal(alice.address);257 });258 });259260 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);266267 const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);268 await submitTransactionAsync(alice, transferTx);269270 const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);271 const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);272273 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);274 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);275 });276 });277278 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);284285 const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);286 await submitTransactionAsync(alice, transferTx);287288 const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);289 const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);290291 expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);292 expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);293 });294 });295});tests/src/util/helpers.tsdiffbeforeafterboth905 await usingApi(async (api: ApiPromise) => {905 await usingApi(async (api: ApiPromise) => {906 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);906 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);907 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;907 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;908 if (events && Array.isArray(events)) {909 const result = getCreateCollectionResult(events);908 const result = getGenericResult(events);909 // if (events && Array.isArray(events)) {910 // const result = getCreateCollectionResult(events);910 // tslint:disable-next-line:no-unused-expression911 // tslint:disable-next-line:no-unused-expression911 expect(result.success).to.be.false;912 expect(result.success).to.be.false;912 }913 //}913 });914 });914}915}915916