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

difftreelog

Merge branch 'develop' into feature/core-214

Igor Kozyrev2021-11-22parents: #9052adc #5846f9c.patch.diff
in: master

24 files changed

modifiedCargo.lockdiffbeforeafterboth
5097 "frame-system-rpc-runtime-api",5097 "frame-system-rpc-runtime-api",
5098 "hex-literal",5098 "hex-literal",
5099 "nft-data-structs",5099 "nft-data-structs",
5100 "orml-vesting",
5100 "pallet-aura",5101 "pallet-aura",
5101 "pallet-balances",5102 "pallet-balances",
5102 "pallet-common",5103 "pallet-common",
5109 "pallet-fungible",5110 "pallet-fungible",
5110 "pallet-inflation",5111 "pallet-inflation",
5111 "pallet-nft",5112 "pallet-nft",
5112 "pallet-nft-transaction-payment",
5113 "pallet-nonfungible",5113 "pallet-nonfungible",
5114 "pallet-randomness-collective-flip",5114 "pallet-randomness-collective-flip",
5115 "pallet-refungible",5115 "pallet-refungible",
5120 "pallet-transaction-payment-rpc-runtime-api",5120 "pallet-transaction-payment-rpc-runtime-api",
5121 "pallet-treasury",5121 "pallet-treasury",
5122 "pallet-unq-scheduler",5122 "pallet-unq-scheduler",
5123 "pallet-vesting",
5124 "pallet-xcm",5123 "pallet-xcm",
5125 "parachain-info",5124 "parachain-info",
5126 "parity-scale-codec",5125 "parity-scale-codec",
5142 "sp-transaction-pool",5141 "sp-transaction-pool",
5143 "sp-version",5142 "sp-version",
5144 "substrate-wasm-builder",5143 "substrate-wasm-builder",
5144 "up-evm-mapping",
5145 "up-rpc",5145 "up-rpc",
5146 "xcm",5146 "xcm",
5147 "xcm-builder",5147 "xcm-builder",
5307 "num-traits",5307 "num-traits",
5308]5308]
5309
5310[[package]]
5311name = "orml-vesting"
5312version = "0.4.1-dev"
5313source = "git+https://github.com/UniqueNetwork/open-runtime-module-library#d69f226e332ae29b7b33d53d2f06f309d2986ea0"
5314dependencies = [
5315 "frame-support",
5316 "frame-system",
5317 "parity-scale-codec",
5318 "scale-info",
5319 "serde",
5320 "sp-io",
5321 "sp-runtime",
5322 "sp-std",
5323]
53095324
5310[[package]]5325[[package]]
5311name = "owning_ref"5326name = "owning_ref"
5575 "sp-core",5590 "sp-core",
5576 "sp-runtime",5591 "sp-runtime",
5577 "sp-std",5592 "sp-std",
5593 "up-evm-mapping",
5578]5594]
55795595
5580[[package]]5596[[package]]
5824 "sp-io",5840 "sp-io",
5825 "sp-runtime",5841 "sp-runtime",
5826 "sp-std",5842 "sp-std",
5843 "up-evm-mapping",
5827 "up-sponsorship",5844 "up-sponsorship",
5828]5845]
58295846
6072 "sp-io",6089 "sp-io",
6073 "sp-runtime",6090 "sp-runtime",
6074 "sp-std",6091 "sp-std",
6092 "up-evm-mapping",
6075 "up-sponsorship",6093 "up-sponsorship",
6076]6094]
6077
6078[[package]]
6079name = "pallet-nft-transaction-payment"
6080version = "3.0.0"
6081dependencies = [
6082 "frame-benchmarking",
6083 "frame-support",
6084 "frame-system",
6085 "pallet-transaction-payment",
6086 "parity-scale-codec",
6087 "scale-info",
6088 "serde",
6089 "sp-core",
6090 "sp-io",
6091 "sp-runtime",
6092 "sp-std",
6093 "up-sponsorship",
6094]
60956095
6096[[package]]6096[[package]]
6097name = "pallet-nicks"6097name = "pallet-nicks"
11831source = "registry+https://github.com/rust-lang/crates.io-index"11831source = "registry+https://github.com/rust-lang/crates.io-index"
11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"11832checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
11833
11834[[package]]
11835name = "up-evm-mapping"
11836version = "0.1.0"
11837dependencies = [
11838 "frame-support",
11839 "sp-core",
11840]
1183311841
11834[[package]]11842[[package]]
11835name = "up-rpc"11843name = "up-rpc"
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
15sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }15sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
16sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }16sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
17sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }17sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
18up-evm-mapping = { default-features = false, path = '../../primitives/evm-mapping' }
18nft-data-structs = { default-features = false, path = '../../primitives/nft' }19nft-data-structs = { default-features = false, path = '../../primitives/nft' }
19pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }20pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
20evm-coder = { default-features = false, path = '../../crates/evm-coder' }21evm-coder = { default-features = false, path = '../../crates/evm-coder' }
32 "frame-system/std",32 "frame-system/std",
33 "sp-runtime/std",33 "sp-runtime/std",
34 "sp-std/std",34 "sp-std/std",
35 "up-evm-mapping/std",
35 "nft-data-structs/std",36 "nft-data-structs/std",
36 "pallet-evm/std",37 "pallet-evm/std",
37]38]
modifiedpallets/common/src/account.rsdiffbeforeafterboth
2use codec::{Encode, EncodeLike, Decode};2use codec::{Encode, EncodeLike, Decode};
3use sp_core::H160;3use sp_core::H160;
4use scale_info::{Type, TypeInfo};4use scale_info::{Type, TypeInfo};
5use sp_core::crypto::AccountId32;
6use core::cmp::Ordering;5use core::cmp::Ordering;
7use serde::{Serialize, Deserialize};6use serde::{Serialize, Deserialize};
8use pallet_evm::AddressMapping;7use pallet_evm::AddressMapping;
9use sp_std::vec::Vec;8use sp_std::vec::Vec;
10use sp_std::clone::Clone;9use sp_std::clone::Clone;
10use up_evm_mapping::EvmBackwardsAddressMapping;
1111
12pub trait CrossAccountId<AccountId>:12pub trait CrossAccountId<AccountId>:
13 Encode + EncodeLike + Decode + TypeInfo + Clone + PartialEq + Ord + core::fmt::Debug + Default13 Encode + EncodeLike + Decode + TypeInfo + Clone + PartialEq + Ord + core::fmt::Debug + Default
177 }177 }
178}178}
179
180pub trait EvmBackwardsAddressMapping<AccountId> {
181 fn from_account_id(account_id: AccountId) -> H160;
182}
183
184/// Should have same mapping as EnsureAddressTruncated
185pub struct MapBackwardsAddressTruncated;
186impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
187 fn from_account_id(account_id: AccountId32) -> H160 {
188 let mut out = [0; 20];
189 out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
190 H160(out)
191 }
192}
193179
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
141pub mod pallet {141pub mod pallet {
142 use super::*;142 use super::*;
143 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};143 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};
144 use account::{EvmBackwardsAddressMapping, CrossAccountId};144 use account::CrossAccountId;
145 use frame_support::traits::Currency;145 use frame_support::traits::Currency;
146 use nft_data_structs::TokenId;146 use nft_data_structs::TokenId;
147 use scale_info::TypeInfo;147 use scale_info::TypeInfo;
153 type CrossAccountId: CrossAccountId<Self::AccountId>;153 type CrossAccountId: CrossAccountId<Self::AccountId>;
154154
155 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;155 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
156 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;156 type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;
157157
158 type Currency: Currency<Self::AccountId>;158 type Currency: Currency<Self::AccountId>;
159 type CollectionCreationPrice: Get<159 type CollectionCreationPrice: Get<
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
5959
60 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {60 fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
61 self.0.consume_sload()?;61 self.0.consume_sload()?;
62 Ok(<Pallet<T>>::allowed(contract_address, user, true))62 Ok(<Pallet<T>>::allowed(contract_address, user)
63 || !<AllowlistEnabled<T>>::get(contract_address))
63 }64 }
6465
65 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {66 fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
113 value: sp_core::U256,114 value: sp_core::U256,
114 ) -> Option<PrecompileOutput> {115 ) -> Option<PrecompileOutput> {
115 // TODO: Extract to another OnMethodCall handler116 // TODO: Extract to another OnMethodCall handler
116 if !<Pallet<T>>::allowed(*target, *source, true) {117 if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {
117 return Some(PrecompileOutput {118 return Some(PrecompileOutput {
118 exit_status: ExitReason::Revert(ExitRevert::Reverted),119 exit_status: ExitReason::Revert(ExitRevert::Reverted),
119 cost: 0,120 cost: 0,
151pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);152pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
152impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {153impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
153 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {154 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
154 if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {155 if !<SelfSponsoring<T>>::get(&call.0) {
156 return None;
157 }
158 if !<Pallet<T>>::allowed(call.0, *who) {
159 return None;
160 }
155 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;161 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
162
156 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {163 if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
157 let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);164 let limit = <SponsoringRateLimit<T>>::get(&call.0);
165
158 let limit_time = last_tx_block + rate_limit;166 let timeout = last_tx_block + limit.into();
159167 if block_number < timeout {
160 if block_number > limit_time {168 return None;
161 <SponsorBasket<T>>::insert(&call.0, who, block_number);169 }
162 return Some(call.0);
163 }
164 } else {170 }
171
165 <SponsorBasket<T>>::insert(&call.0, who, block_number);172 <SponsorBasket<T>>::insert(&call.0, who, block_number);
173
166 return Some(call.0);174 Some(call.0)
167 }
168 }
169 None
170 }175 }
171}176}
172177
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);76 <SponsoringRateLimit<T>>::insert(contract, rate_limit);
77 }77 }
7878
79 /// Default is returned if allowlist is disabled
80 pub fn allowed(contract: H160, user: H160, default: bool) -> bool {79 pub fn allowed(contract: H160, user: H160) -> bool {
81 if !<AllowlistEnabled<T>>::get(contract) {
82 return default;
83 }
84 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user80 <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
85 }81 }
8682
modifiedpallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth
15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }15fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }16pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 17up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
18up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
1819
19[dependencies.codec]20[dependencies.codec]
20default-features = false21default-features = false
35 "pallet-ethereum/std",36 "pallet-ethereum/std",
36 "fp-evm/std",37 "fp-evm/std",
37 "up-sponsorship/std",38 "up-sponsorship/std",
39 "up-evm-mapping/std",
38]40]
3941
modifiedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]
22
3pub use pallet::*;
4
5#[frame_support::pallet]
6pub mod pallet {
7 use core::marker::PhantomData;3use core::marker::PhantomData;
4use fp_evm::WithdrawReason;
8 use frame_support::traits::Currency;5use frame_support::traits::{Currency, IsSubType};
9 use pallet_evm::EVMCurrencyAdapter;6pub use pallet::*;
10 use fp_evm::WithdrawReason;7use pallet_evm::{EVMCurrencyAdapter, EnsureAddressOrigin};
11 use sp_core::{H160, U256};8use sp_core::{H160, U256};
12 use sp_runtime::TransactionOutcome;9use sp_runtime::TransactionOutcome;
13 use up_sponsorship::SponsorshipHandler;10use up_sponsorship::SponsorshipHandler;
11use up_evm_mapping::EvmBackwardsAddressMapping;
12use pallet_evm::AddressMapping;
13
14#[frame_support::pallet]
15pub mod pallet {
16 use super::*;
17
14 use sp_std::vec::Vec;18 use frame_support::traits::Currency;
15
16 type NegativeImbalanceOf<C, T> =
17 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;19 use sp_std::vec::Vec;
1820
19 #[pallet::config]21 #[pallet::config]
20 pub trait Config: frame_system::Config {22 pub trait Config: frame_system::Config {
21 type SponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;23 type EvmSponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
22 type Currency: Currency<Self::AccountId>;24 type Currency: Currency<Self::AccountId>;
25 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
26 type EvmAddressMapping: AddressMapping<Self::AccountId>;
23 }27 }
2428
25 #[pallet::pallet]29 #[pallet::pallet]
26 #[pallet::generate_store(pub(super) trait Store)]30 #[pallet::generate_store(pub(super) trait Store)]
27 pub struct Pallet<T>(_);31 pub struct Pallet<T>(_);
32}
33
34type NegativeImbalanceOf<C, T> =
35 <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
2836
29 pub struct ChargeEvmLiquidityInfo<T>37pub struct ChargeEvmLiquidityInfo<T>
30 where38where
42 WithdrawReason::Call { target, input } => {50 WithdrawReason::Call { target, input } => {
43 // This method is only used for checking, we shouldn't touch storage in it51 // This method is only used for checking, we shouldn't touch storage in it
44 frame_support::storage::with_transaction(|| {52 frame_support::storage::with_transaction(|| {
45 TransactionOutcome::Rollback(T::SponsorshipHandler::get_sponsor(53 TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
46 &origin,54 &origin,
47 &(*target, input.clone()),55 &(*target, input.clone()),
48 ))56 ))
68 ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {75 ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
69 let mut who_pays_fee = *who;76 let mut who_pays_fee = *who;
70 if let WithdrawReason::Call { target, input } = &reason {77 if let WithdrawReason::Call { target, input } = &reason {
71 who_pays_fee = T::SponsorshipHandler::get_sponsor(who, &(*target, input.clone()))78 who_pays_fee = T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
72 .unwrap_or(who_pays_fee);79 .unwrap_or(who_pays_fee);
73 }80 }
74 let negative_imbalance =81 let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
95 )101 )
96 }102 }
97 }103}
98}104
105/// Implements sponsoring for evm calls performed from pallet-evm (via api.tx.ethereum.transact/api.tx.evm.call)
106pub struct BridgeSponsorshipHandler<T>(PhantomData<T>);
107impl<T, C> SponsorshipHandler<T::AccountId, C> for BridgeSponsorshipHandler<T>
108where
109 T: Config + pallet_evm::Config,
110 C: IsSubType<pallet_evm::Call<T>>,
111{
112 fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
113 match call.is_sub_type()? {
114 pallet_evm::Call::call {
115 source,
116 target,
117 input,
118 ..
119 } => {
120 let _ = T::CallOrigin::ensure_address_origin(
121 source,
122 <frame_system::RawOrigin<T::AccountId>>::Signed(who.clone()).into(),
123 )
124 .ok()?;
125 let who = T::EvmBackwardsAddressMapping::from_account_id(who.clone());
126 // Effects from EvmSponsorshipHandler are applied in OnChargeEvmTransaction by pallet_evm::runner
127 // TODO: Should we implement simulation mode (test, but do not apply effects) in `up-sponsorship`?
128 let sponsor = frame_support::storage::with_transaction(|| {
129 TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
130 &who,
131 &(target.clone(), input.clone()),
132 ))
133 })?;
134 let sponsor = T::EvmAddressMapping::into_account_id(sponsor);
135 Some(sponsor)
136 }
137 _ => None,
138 }
139 }
140}
99141
deletedpallets/nft-transaction-payment/Cargo.tomldiffbeforeafterboth

no changes

deletedpallets/nft-transaction-payment/README.mddiffbeforeafterboth

no changes

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

no changes

modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
34 'fp-evm/std',34 'fp-evm/std',
35 'nft-data-structs/std',35 'nft-data-structs/std',
36 'up-sponsorship/std',36 'up-sponsorship/std',
37 'up-evm-mapping/std',
37 'sp-std/std',38 'sp-std/std',
38 'sp-api/std',39 'sp-api/std',
39 'sp-runtime/std',40 'sp-runtime/std',
135sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.12" }136sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.12" }
136137
137up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" } 138up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
138139up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
139evm-coder = { default-features = false, path = "../../crates/evm-coder" }140evm-coder = { default-features = false, path = "../../crates/evm-coder" }
140pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }141pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
141primitive-types = { version = "0.10.1", default-features = false, features = [142primitive-types = { version = "0.10.1", default-features = false, features = [
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction1//! Implements EVM sponsoring logic via OnChargeEVMTransaction
22
3use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};3use crate::{Config, sponsorship::*};
4use evm_coder::{Call, abi::AbiReader};4use evm_coder::{Call, abi::AbiReader};
5use frame_support::{
6 storage::{StorageDoubleMap},
7};
8use pallet_common::eth::map_eth_to_id;5use pallet_common::{CollectionHandle, eth::map_eth_to_id};
9use sp_core::H160;6use sp_core::H160;
10use sp_std::prelude::*;7use sp_std::prelude::*;
11use up_sponsorship::SponsorshipHandler;8use up_sponsorship::SponsorshipHandler;
12use core::marker::PhantomData;9use core::marker::PhantomData;
13use core::convert::TryInto;10use core::convert::TryInto;
14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};11use nft_data_structs::TokenId;
15use pallet_common::{12use up_evm_mapping::EvmBackwardsAddressMapping;
16 CollectionById,13use pallet_evm::AddressMapping;
17 account::{CrossAccountId, EvmBackwardsAddressMapping},
18};
1914
20use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};15use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};16use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
22
23struct AnyError;
24
25fn try_sponsor<T: Config>(
26 caller: &H160,
27 collection_id: CollectionId,
28 collection: &Collection<T>,
29 call: &[u8],
30) -> Result<(), AnyError> {
31 let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
32 match &collection.mode {
33 crate::CollectionMode::NFT => {
34 let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)
35 .map_err(|_| AnyError)?
36 .ok_or(AnyError)?;
37 match call {
38 UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {
39 token_id,
40 ..
41 })
42 | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
43 let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
44 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
45 let collection_limits = &collection.limits;
46 let limit =
47 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
48
49 let mut sponsor = true;
50 if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
51 let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);
52 let limit_time = last_tx_block + limit.into();
53 if block_number <= limit_time {
54 sponsor = false;
55 }
56 }
57 if sponsor {
58 <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);
59 return Ok(());
60 }
61 }
62 _ => {}
63 }
64 }
65 crate::CollectionMode::Fungible(_) => {
66 let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)
67 .map_err(|_| AnyError)?
68 .ok_or(AnyError)?;
69 #[allow(clippy::single_match)]
70 match call {
71 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
72 let who = T::CrossAccountId::from_eth(*caller);
73 let collection_limits = &collection.limits;
74 let limit = collection_limits
75 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
76
77 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
78 let mut sponsored = true;
79 if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {
80 let last_tx_block =
81 <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());
82 let limit_time = last_tx_block + limit.into();
83 if block_number <= limit_time {
84 sponsored = false;
85 }
86 }
87 if sponsored {
88 <FungibleTransferBasket<T>>::insert(
89 collection_id,
90 who.as_sub(),
91 block_number,
92 );
93 return Ok(());
94 }
95 }
96 _ => {}
97 }
98 }
99 _ => {}
100 }
101 Err(AnyError)
102}
10317
104pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);18pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
105impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {19impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
106 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {20 fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
107 if let Some(collection_id) = map_eth_to_id(&call.0) {21 let collection_id = map_eth_to_id(&call.0)?;
108 if let Some(collection) = <CollectionById<T>>::get(collection_id) {22 let collection = <CollectionHandle<T>>::new(collection_id)?;
109 if !collection.sponsorship.confirmed() {23 let sponsor = collection.sponsorship.sponsor()?.clone();
110 return None;24 let sponsor =
111 }25 <T as pallet_common::Config>::EvmBackwardsAddressMapping::from_account_id(sponsor);
26 let who = <T 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(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}
12465
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
136 //#region Tokens transfer rate limit baskets136 //#region Tokens transfer rate limit baskets
137 /// (Collection id (controlled?2), who created (real))137 /// (Collection id (controlled?2), who created (real))
138 /// TODO: Off chain worker should remove from this map when collection gets removed138 /// TODO: Off chain worker should remove from this map when collection gets removed
139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;139 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
140 /// Collection id (controlled?2), token id (controlled?2)140 /// Collection id (controlled?2), token id (controlled?2)
141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;141 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
142 /// Collection id (controlled?2), owning user (real)142 /// Collection id (controlled?2), owning user (real)
143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;143 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
144 /// Collection id (controlled?2), token id (controlled?2)144 /// Collection id (controlled?2), token id (controlled?2)
145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;145 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
146 //#endregion146 //#endregion
147147
148 /// Variable metadata sponsoring148 /// Variable metadata sponsoring
149 /// Collection id (controlled?2), token id (controlled?2)149 /// Collection id (controlled?2), token id (controlled?2)
150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;150 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
151 /// Approval sponsoring
152 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
153 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
154 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
151 }155 }
152}156}
153157
249253
250 <NftTransferBasket<T>>::remove_prefix(collection_id, None);254 <NftTransferBasket<T>>::remove_prefix(collection_id, None);
251 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);255 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
252 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);256 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);
253257
254 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);258 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
259 <NftApproveBasket<T>>::remove_prefix(collection_id, None);
260 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);
261 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);
255262
256 Ok(())263 Ok(())
257 }264 }
592 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {599 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
593 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
594601
595 dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))602 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;
603 if value == 1 {
604 <NftTransferBasket<T>>::remove(collection_id, item_id);
605 <NftApproveBasket<T>>::remove(collection_id, item_id);
606 }
607 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?
608 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());
609 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));
610 Ok(post_info)
596 }611 }
597612
598 /// Destroys a concrete instance of NFT on behalf of the owner613 /// Destroys a concrete instance of NFT on behalf of the owner
modifiedpallets/nft/src/sponsorship.rsdiffbeforeafterboth
1use crate::{1use crate::{
2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,2 Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode,3 FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
4 FungibleApproveBasket, RefungibleApproveBasket,
4};5};
5use core::marker::PhantomData;6use core::marker::PhantomData;
6use up_sponsorship::SponsorshipHandler;7use up_sponsorship::SponsorshipHandler;
7use frame_support::{8use frame_support::{
8 traits::{IsSubType},9 traits::{IsSubType},
9 storage::{StorageMap, StorageDoubleMap},10 storage::{StorageMap, StorageDoubleMap, StorageNMap},
10};11};
11use nft_data_structs::{12use nft_data_structs::{
12 TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,13 CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, 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};
17
18pub fn withdraw_transfer<T: Config>(
19 collection: &CollectionHandle<T>,
20 who: &T::AccountId,
21 item_id: &TokenId,
22) -> Option<()> {
23 // sponsor timeout
24 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
25 let limit = collection
26 .limits
27 .sponsor_transfer_timeout(match collection.mode {
28 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
29 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
30 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
31 });
32
33 let last_tx_block = match collection.mode {
34 CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
35 CollectionMode::Fungible(_) => <FungibleTransferBasket<T>>::get(collection.id, who),
36 CollectionMode::ReFungible => {
37 <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who))
38 }
39 };
40
41 if let Some(last_tx_block) = last_tx_block {
42 let timeout = last_tx_block + limit.into();
43 if block_number < timeout {
44 return None;
45 }
46 }
47
48 match collection.mode {
49 CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
50 CollectionMode::Fungible(_) => {
51 <FungibleTransferBasket<T>>::insert(collection.id, who, block_number)
52 }
53 CollectionMode::ReFungible => {
54 <ReFungibleTransferBasket<T>>::insert((collection.id, item_id, who), block_number)
55 }
56 };
57
58 Some(())
59}
60
61pub fn withdraw_create_item<T: Config>(
62 collection: &CollectionHandle<T>,
63 who: &T::AccountId,
64 _properties: &CreateItemData,
65) -> Option<()> {
66 if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
67 return None;
68 }
69
70 // sponsor timeout
71 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
72 let limit = collection
73 .limits
74 .sponsor_transfer_timeout(match _properties {
75 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
76 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
77 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
78 });
79
80 if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {
81 let timeout = last_tx_block + limit.into();
82 if block_number < timeout {
83 return None;
84 }
85 }
86
87 CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);
88
89 Some(())
90}
91
92pub fn withdraw_set_variable_meta_data<T: Config>(
93 collection: &CollectionHandle<T>,
94 item_id: &TokenId,
95 data: &[u8],
96) -> Option<()> {
97 // Can't sponsor fungible collection, this tx will be rejected
98 // as invalid
99 if matches!(collection.mode, CollectionMode::Fungible(_)) {
100 return None;
101 }
102 if data.len() > collection.limits.sponsored_data_size() as usize {
103 return None;
104 }
105
106 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
107 let limit = collection.limits.sponsored_data_rate_limit()?;
108
109 if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
110 let timeout = last_tx_block + limit.into();
111 if block_number < timeout {
112 return None;
113 }
114 }
115
116 <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
117
118 Some(())
119}
120
121pub fn withdraw_approve<T: Config>(
122 collection: &CollectionHandle<T>,
123 who: &T::AccountId,
124 item_id: &TokenId,
125) -> Option<()> {
126 // sponsor timeout
127 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
128 let limit = collection.limits.sponsor_approve_timeout();
129
130 let last_tx_block = match collection.mode {
131 CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
132 CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
133 CollectionMode::ReFungible => {
134 <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
135 }
136 };
137
138 if let Some(last_tx_block) = last_tx_block {
139 let timeout = last_tx_block + limit.into();
140 if block_number < timeout {
141 return None;
142 }
143 }
144
145 match collection.mode {
146 CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
147 CollectionMode::Fungible(_) => {
148 <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
149 }
150 CollectionMode::ReFungible => {
151 <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
152 }
153 };
154
155 Some(())
156}
157
158fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
159 let collection = CollectionHandle::new(id)?;
160 let sponsor = collection.sponsorship.sponsor().cloned()?;
161 Some((sponsor, collection))
162}
16163
17pub struct NftSponsorshipHandler<T>(PhantomData<T>);164pub struct NftSponsorshipHandler<T>(PhantomData<T>);
18impl<T: Config> NftSponsorshipHandler<T> {
19 pub fn withdraw_create_item(
20 who: &T::AccountId,
21 collection_id: &CollectionId,
22 _properties: &CreateItemData,
23 ) -> Option<T::AccountId> {
24 let collection = CollectionById::<T>::get(collection_id)?;
25
26 // sponsor timeout
27 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
28
29 let limit = collection
30 .limits
31 .sponsor_transfer_timeout(match _properties {
32 CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
33 CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
34 CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
35 });
36 if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
37 let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
38 let limit_time = last_tx_block + limit.into();
39 if block_number <= limit_time {
40 return None;
41 }
42 }
43 CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
44
45 // check free create limit
46 if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
47 collection.sponsorship.sponsor().cloned()
48 } else {
49 None
50 }
51 }
52
53 pub fn withdraw_transfer(
54 who: &T::AccountId,
55 collection_id: &CollectionId,
56 item_id: &TokenId,
57 ) -> Option<T::AccountId> {
58 let collection = CollectionById::<T>::get(collection_id)?;
59
60 let mut sponsor_transfer = false;
61 if collection.sponsorship.confirmed() {
62 let collection_limits = collection.limits.clone();
63 let collection_mode = collection.mode.clone();
64
65 // sponsor timeout
66 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
67 sponsor_transfer = match collection_mode {
68 CollectionMode::NFT => {
69 // get correct limit
70 let limit =
71 collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
72
73 let mut sponsored = true;
74 if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
75 let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);
76 let limit_time = last_tx_block + limit.into();
77 if block_number <= limit_time {
78 sponsored = false;
79 }
80 }
81 if sponsored {
82 NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
83 }
84
85 sponsored
86 }
87 CollectionMode::Fungible(_) => {
88 // get correct limit
89 let limit = collection_limits
90 .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
91
92 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
93 let mut sponsored = true;
94 if FungibleTransferBasket::<T>::contains_key(collection_id, who) {
95 let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);
96 let limit_time = last_tx_block + limit.into();
97 if block_number <= limit_time {
98 sponsored = false;
99 }
100 }
101 if sponsored {
102 FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
103 }
104
105 sponsored
106 }
107 CollectionMode::ReFungible => {
108 // get correct limit
109 let limit = collection_limits
110 .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
111
112 let mut sponsored = true;
113 if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
114 let last_tx_block =
115 ReFungibleTransferBasket::<T>::get(collection_id, item_id);
116 let limit_time = last_tx_block + limit.into();
117 if block_number <= limit_time {
118 sponsored = false;
119 }
120 }
121 if sponsored {
122 ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
123 }
124
125 sponsored
126 }
127 };
128 }
129
130 if !sponsor_transfer {
131 None
132 } else {
133 collection.sponsorship.sponsor().cloned()
134 }
135 }
136
137 pub fn withdraw_set_variable_meta_data(
138 collection_id: &CollectionId,
139 item_id: &TokenId,
140 data: &[u8],
141 ) -> Option<T::AccountId> {
142 let mut sponsor_metadata_changes = false;
143
144 let collection = CollectionById::<T>::get(collection_id)?;
145
146 if collection.sponsorship.confirmed() &&
147 // Can't sponsor fungible collection, this tx will be rejected
148 // as invalid
149 !matches!(collection.mode, CollectionMode::Fungible(_)) &&
150 data.len() <= collection.limits.sponsored_data_size() as usize
151 {
152 if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
153 let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
154
155 if VariableMetaDataBasket::<T>::get(collection_id, item_id)
156 .map(|last_block| block_number - last_block > rate_limit.into())
157 .unwrap_or(true)
158 {
159 sponsor_metadata_changes = true;
160 VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
161 }
162 }
163 }
164
165 if !sponsor_metadata_changes {
166 None
167 } else {
168 collection.sponsorship.sponsor().cloned()
169 }
170 }
171}
172
173impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>165impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
174where166where
181 collection_id,173 collection_id,
182 data,174 data,
183 ..175 ..
184 } => Self::withdraw_create_item(who, collection_id, data),176 } => {
177 let (sponsor, collection) = load(*collection_id)?;
178 withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)
179 }
185 Call::transfer {180 Call::transfer {
186 collection_id,181 collection_id,
187 item_id,182 item_id,
188 ..183 ..
189 } => Self::withdraw_transfer(who, collection_id, item_id),184 }
185 | Call::transfer_from {
186 collection_id,
187 item_id,
188 ..
189 } => {
190 let (sponsor, collection) = load(*collection_id)?;
191 withdraw_transfer::<T>(&collection, who, item_id).map(|()| sponsor)
192 }
193 Call::approve {
194 collection_id,
195 item_id,
196 ..
197 } => {
198 let (sponsor, collection) = load(*collection_id)?;
199 withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
200 }
190 Call::set_variable_meta_data {201 Call::set_variable_meta_data {
191 collection_id,202 collection_id,
192 item_id,203 item_id,
193 data,204 data,
194 } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),205 } => {
206 let (sponsor, collection) = load(*collection_id)?;
207 withdraw_set_variable_meta_data::<T>(&collection, item_id, data).map(|()| sponsor)
208 }
195 _ => None,209 _ => None,
196 }210 }
197 }211 }
addedprimitives/evm-mapping/Cargo.tomldiffbeforeafterboth

no changes

addedprimitives/evm-mapping/src/lib.rsdiffbeforeafterboth

no changes

modifiedprimitives/nft/src/lib.rsdiffbeforeafterboth
54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;54pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;55pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
56
57pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;
5658
57// Schema limits59// Schema limits
58pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;60pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;
246 pub variable_data: Vec<u8>,248 pub variable_data: Vec<u8>,
247}249}
248250
251/// All fields are wrapped in `Option`s, where None means chain default
249#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]252#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]
250#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]253#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
251pub struct CollectionLimits {254pub struct CollectionLimits {
254 /// None - setVariableMetadata is not sponsored257 /// None - setVariableMetadata is not sponsored
255 /// Some(v) - setVariableMetadata is sponsored258 /// Some(v) - setVariableMetadata is sponsored
256 /// if there is v block between txs259 /// if there is v block between txs
257 pub sponsored_data_rate_limit: Option<u32>,260 pub sponsored_data_rate_limit: Option<(Option<u32>,)>,
258 pub token_limit: Option<u32>,261 pub token_limit: Option<u32>,
259262
260 // Timeouts for item types in passed blocks263 // Timeouts for item types in passed blocks
261 pub sponsor_transfer_timeout: Option<u32>,264 pub sponsor_transfer_timeout: Option<u32>,
265 pub sponsor_approve_timeout: Option<u32>,
262 pub owner_can_transfer: Option<bool>,266 pub owner_can_transfer: Option<bool>,
263 pub owner_can_destroy: Option<bool>,267 pub owner_can_destroy: Option<bool>,
264 pub transfers_enabled: Option<bool>,268 pub transfers_enabled: Option<bool>,
285 .unwrap_or(default)289 .unwrap_or(default)
286 .min(MAX_SPONSOR_TIMEOUT)290 .min(MAX_SPONSOR_TIMEOUT)
287 }291 }
292 pub fn sponsor_approve_timeout(&self) -> u32 {
293 self.sponsor_approve_timeout
294 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)
295 .min(MAX_SPONSOR_TIMEOUT)
296 }
288 pub fn owner_can_transfer(&self) -> bool {297 pub fn owner_can_transfer(&self) -> bool {
289 self.owner_can_transfer.unwrap_or(true)298 self.owner_can_transfer.unwrap_or(true)
290 }299 }
296 }305 }
297 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {306 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {
298 self.sponsored_data_rate_limit307 self.sponsored_data_rate_limit
308 .unwrap_or((None,))
309 .0
299 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))310 .map(|v| v.min(MAX_SPONSOR_TIMEOUT))
300 }311 }
301}312}
modifiedruntime/Cargo.tomldiffbeforeafterboth
69 'pallet-ethereum/std',69 'pallet-ethereum/std',
70 'fp-rpc/std',70 'fp-rpc/std',
71 'up-rpc/std',71 'up-rpc/std',
72 'up-evm-mapping/std',
72 'fp-self-contained/std',73 'fp-self-contained/std',
73 'parachain-info/std',74 'parachain-info/std',
74 'serde',75 'serde',
80 'pallet-nft/std',81 'pallet-nft/std',
81 'pallet-unq-scheduler/std',82 'pallet-unq-scheduler/std',
82 'pallet-nft-charge-transaction/std',83 'pallet-nft-charge-transaction/std',
83 'pallet-nft-transaction-payment/std',
84 'nft-data-structs/std',84 'nft-data-structs/std',
85 'sp-api/std',85 'sp-api/std',
86 'sp-block-builder/std',86 'sp-block-builder/std',
362default-features = false362default-features = false
363363
364[dependencies.orml-vesting]364[dependencies.orml-vesting]
365git = "https://github.com/open-web3-stack/open-runtime-module-library"365git = 'https://github.com/UniqueNetwork/open-runtime-module-library'
366version = "0.4.1-dev" 366version = "0.4.1-dev"
367default-features = false367default-features = false
368368
376derivative = "2.2.0"376derivative = "2.2.0"
377pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }377pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
378up-rpc = { path = "../primitives/rpc", default-features = false }378up-rpc = { path = "../primitives/rpc", default-features = false }
379up-evm-mapping = { path = "../primitives/evm-mapping", default-features = false }
379pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }380pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
380nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }381nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
381pallet-common = { default-features = false, path = "../pallets/common" }382pallet-common = { default-features = false, path = "../pallets/common" }
384pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }385pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }
385pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }386pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }
386# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }387# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
387pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
388pallet-nft-charge-transaction = {git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }388pallet-nft-charge-transaction = { git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }
389pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }389pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }
390pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }390pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }
modifiedruntime/src/lib.rsdiffbeforeafterboth
736736
737impl pallet_common::Config for Runtime {737impl pallet_common::Config for Runtime {
738 type Event = Event;738 type Event = Event;
739 type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;739 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;
742742
777 pub const MaxScheduledPerBlock: u32 = 50;777 pub const MaxScheduledPerBlock: u32 = 50;
778}778}
779779
780pub struct Sponsoring;
781impl SponsoringResolve<AccountId, Call> for Sponsoring {
782 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>
783 where
784 Call: Dispatchable<Info = DispatchInfo>,780type EvmSponsorshipHandler = (
785 AccountId: AsRef<[u8]>,781 pallet_nft::NftEthSponsorshipHandler<Runtime>,
786 {
787 pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)782 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
788 }783);
789}
790
791type SponsorshipHandler = (784type SponsorshipHandler = (
792 pallet_nft::NftSponsorshipHandler<Runtime>,785 pallet_nft::NftSponsorshipHandler<Runtime>,
793 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,786 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
787 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
794);788);
795789
796impl pallet_unq_scheduler::Config for Runtime {790impl pallet_unq_scheduler::Config for Runtime {
805 type WeightInfo = ();799 type WeightInfo = ();
806}800}
807
808impl pallet_nft_transaction_payment::Config for Runtime {
809 type SponsorshipHandler = SponsorshipHandler;
810}
811801
812impl pallet_evm_transaction_payment::Config for Runtime {802impl pallet_evm_transaction_payment::Config for Runtime {
803 type EvmSponsorshipHandler = EvmSponsorshipHandler;
804 type Currency = Balances;
813 type SponsorshipHandler = (805 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
814 pallet_nft::NftEthSponsorshipHandler<Self>,
815 pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,
816 );
817 type Currency = Balances;806 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
818}807}
819808
820impl pallet_nft_charge_transaction::Config for Runtime {809impl pallet_nft_charge_transaction::Config for Runtime {
821 type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;810 type SponsorshipHandler = SponsorshipHandler;
822}811}
823812
824// impl pallet_contract_helpers::Config for Runtime {813// impl pallet_contract_helpers::Config for Runtime {
870 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,859 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
871 Nft: pallet_nft::{Pallet, Call, Storage} = 61,860 Nft: pallet_nft::{Pallet, Call, Storage} = 61,
872 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,861 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
873 NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,862 // free = 63
874 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,863 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,
875 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,864 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
876 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,865 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
251 const zeroBalance = await findUnusedAddress(api);251 const zeroBalance = await findUnusedAddress(api);
252252
253 // Mint token for alice253 // Mint token for alice
254 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);254 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
255255
256 // Transfer this token from Alice to unused address and back
257 // Alice to Zero gets sponsored
258 const aliceToZero = api.tx.nft.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);256 const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
257
258 // Zero to alice gets sponsored
259 const events1 = await submitTransactionAsync(alice, aliceToZero);259 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
260 const result1 = getGenericResult(events1);260 const result1 = getGenericResult(events1);
261 expect(result1.success).to.be.true;261 expect(result1.success).to.be.true;
262262
263 // Second transfer should fail263 // Second transfer should fail
264 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();264 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
265 const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
266 const badTransaction = async function () {
267 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
268 };
269 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');265 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');
270 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();266 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
267 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
271268
272 // Try again after Zero gets some balance - now it should succeed269 // Try again after Zero gets some balance - now it should succeed
273 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);270 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
276 const result2 = getGenericResult(events2);273 const result2 = getGenericResult(events2);
277 expect(result2.success).to.be.true;274 expect(result2.success).to.be.true;
278
279 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
280 });275 });
281 });276 });
282277
modifiedtests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
2import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';2import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';
3import privateKey from '../../substrate/privateKey';3import privateKey from '../../substrate/privateKey';
4import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';4import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';
5import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase} from '../util/helpers';5import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase, transferBalanceToEth} from '../util/helpers';
6import {evmToAddress} from '@polkadot/util-crypto';6import {evmToAddress} from '@polkadot/util-crypto';
7import nonFungibleAbi from '../nonFungibleAbi.json';7import nonFungibleAbi from '../nonFungibleAbi.json';
8import fungibleAbi from '../fungibleAbi.json';8import fungibleAbi from '../fungibleAbi.json';
1212
13describe('Matcher contract usage', () => {13describe('Matcher contract usage', () => {
14 itWeb3('With UNQ', async ({api, web3}) => {14 itWeb3('With UNQ', async ({api, web3}) => {
15 const alice = privateKey('//Alice');
16 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
17 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
15 const matcherOwner = await createEthAccountWithBalance(api, web3);18 const matcherOwner = await createEthAccountWithBalance(api, web3);
19 const helpers = contractHelpers(web3, matcherOwner);
20
16 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {21 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
17 from: matcherOwner,22 from: matcherOwner,
18 ...GAS_ARGS,23 ...GAS_ARGS,
19 });24 });
20 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});25 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});
21 const helpers = contractHelpers(web3, matcherOwner);26
22 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});27 await transferBalanceToEth(api, alice, matcher.options.address);
23 await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});28 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
2429
25 const alice = privateKey('//Alice');
26 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});30 await transferBalanceToEth(api, alice, subToEth(alice.address));
27 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
28 await setCollectionSponsorExpectSuccess(collectionId, alice.address);31 await setCollectionSponsorExpectSuccess(collectionId, alice.address);
29 await confirmSponsorshipExpectSuccess(collectionId);32 await confirmSponsorshipExpectSuccess(collectionId);
3033
31 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(alice.address), true).send({from: matcherOwner});34 const seller = privateKey('//Seller/' + Date.now());
32 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(alice.address)));35 await addToAllowListExpectSuccess(alice, collectionId, {Ethereum:subToEth(seller.address)});
33
34 const seller = privateKey('//Bob');
35 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});36 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
36 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
3737
38 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);38 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
3939
40 // To transfer item to matcher it first needs to be transfered to EVM account of bob40 // To transfer item to matcher it first needs to be transfered to EVM account of seller
41 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});41 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
42
43 // Token is owned by seller initially
44 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});42 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
4543
46 // Ask44 // Ask
55 // Buy53 // Buy
56 {54 {
57 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);55 const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);
58 // There is two functions named 'buy', so we should provide full signature
59 await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), {value: PRICE});56 await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId), {value: PRICE});
60 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);57 expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);
61 }58 }
6259
137 });133 });
138134
139 itWeb3('With escrow', async ({api, web3}) => {135 itWeb3('With escrow', async ({api, web3}) => {
136 const alice = privateKey('//Alice');
137 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
138 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
140 const matcherOwner = await createEthAccountWithBalance(api, web3);139 const matcherOwner = await createEthAccountWithBalance(api, web3);
140 const helpers = contractHelpers(web3, matcherOwner);
141 const escrow = await createEthAccountWithBalance(api, web3);141 const escrow = await createEthAccountWithBalance(api, web3);
142
142 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {143 const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
143 from: matcherOwner,144 from: matcherOwner,
144 ...GAS_ARGS,145 ...GAS_ARGS,
145 });146 });
146 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});147 const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});
147 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});148 await matcher.methods.setEscrow(escrow).send({from: matcherOwner});
148 const helpers = contractHelpers(web3, matcherOwner);149
149 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});150 await transferBalanceToEth(api, alice, matcher.options.address);
150 await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});151 await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
151152
152 const alice = privateKey('//Alice');
153 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});153 await transferBalanceToEth(api, alice, subToEth(alice.address));
154 const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
155 await setCollectionSponsorExpectSuccess(collectionId, alice.address);154 await setCollectionSponsorExpectSuccess(collectionId, alice.address);
156 await confirmSponsorshipExpectSuccess(collectionId);155 await confirmSponsorshipExpectSuccess(collectionId);
157
158 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(alice.address), true).send({from: matcherOwner});
159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(alice.address)));
160156
161 const seller = privateKey('//Bob');157 const seller = privateKey('//Seller/' + Date.now());
162 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});158 await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
163 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));159 await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
164160
165 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);161 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
166162
167 // To transfer item to matcher it first needs to be transfered to EVM account of bob163 // To transfer item to matcher it first needs to be transfered to EVM account of seller
168 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});164 await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
169
170 // Token is owned by seller initially
171 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});165 expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
172166
173 // Ask167 // Ask
modifiedtests/src/eth/util/helpers.tsdiffbeforeafterboth
228 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});228 return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});
229}229}
230230
231/**
232 * Execute ethereum method call using substrate account
233 * @param to target contract
234 * @param mkTx - closure, receiving `contract.methods`, and returning method call,
235 * to be used as following (assuming `to` = erc20 contract):
236 * `m => m.transfer(to, amount)`
237 *
238 * # Example
239 * ```ts
240 * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));
241 * ```
242 */
231export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {243export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {
232 const tx = api.tx.evm.call(244 const tx = api.tx.evm.call(
233 subToEth(from.address),245 subToEth(from.address),
246 return (await getBalance(api, [evmToAddress(address)]))[0];258 return (await getBalance(api, [evmToAddress(address)]))[0];
247}259}
248260
261/**
262 * Measure how much gas given closure consumes
263 *
264 * @param user which user balance will be checked
265 */
249export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {266export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {
250 const before = await ethBalanceViaSub(api, user);267 const before = await ethBalanceViaSub(api, user);
251268
modifiedtests/src/pallet-presence.test.tsdiffbeforeafterboth
39 'nonfungible',39 'nonfungible',
40 'refungible',40 'refungible',
41 'scheduler',41 'scheduler',
42 'nftpayment',
43 'charging',42 'charging',
44];43];
4544