difftreelog
merge develop
in: master
42 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5097,6 +5097,7 @@
"frame-system-rpc-runtime-api",
"hex-literal",
"nft-data-structs",
+ "orml-vesting",
"pallet-aura",
"pallet-balances",
"pallet-common",
@@ -5109,7 +5110,6 @@
"pallet-fungible",
"pallet-inflation",
"pallet-nft",
- "pallet-nft-transaction-payment",
"pallet-nonfungible",
"pallet-randomness-collective-flip",
"pallet-refungible",
@@ -5120,7 +5120,6 @@
"pallet-transaction-payment-rpc-runtime-api",
"pallet-treasury",
"pallet-unq-scheduler",
- "pallet-vesting",
"pallet-xcm",
"parachain-info",
"parity-scale-codec",
@@ -5142,6 +5141,7 @@
"sp-transaction-pool",
"sp-version",
"substrate-wasm-builder",
+ "up-evm-mapping",
"up-rpc",
"xcm",
"xcm-builder",
@@ -5308,6 +5308,21 @@
]
[[package]]
+name = "orml-vesting"
+version = "0.4.1-dev"
+source = "git+https://github.com/UniqueNetwork/open-runtime-module-library#d69f226e332ae29b7b33d53d2f06f309d2986ea0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
+[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5575,6 +5590,7 @@
"sp-core",
"sp-runtime",
"sp-std",
+ "up-evm-mapping",
]
[[package]]
@@ -5824,6 +5840,7 @@
"sp-io",
"sp-runtime",
"sp-std",
+ "up-evm-mapping",
"up-sponsorship",
]
@@ -6072,24 +6089,7 @@
"sp-io",
"sp-runtime",
"sp-std",
- "up-sponsorship",
-]
-
-[[package]]
-name = "pallet-nft-transaction-payment"
-version = "3.0.0"
-dependencies = [
- "frame-benchmarking",
- "frame-support",
- "frame-system",
- "pallet-transaction-payment",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
+ "up-evm-mapping",
"up-sponsorship",
]
@@ -11832,6 +11832,14 @@
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
+name = "up-evm-mapping"
+version = "0.1.0"
+dependencies = [
+ "frame-support",
+ "sp-core",
+]
+
+[[package]]
name = "up-rpc"
version = "0.1.0"
dependencies = [
launch-config.jsondiffbeforeafterboth--- a/launch-config.json
+++ b/launch-config.json
@@ -9,7 +9,6 @@
"rpcPort": 9843,
"port": 30444,
"flags": [
- "--rpc-port=9843",
"-lparachain::candidate_validation=debug"
]
},
@@ -19,7 +18,6 @@
"rpcPort": 9854,
"port": 30555,
"flags": [
- "--rpc-port=9854",
"-lparachain::candidate_validation=debug"
]
},
@@ -29,7 +27,6 @@
"rpcPort": 9865,
"port": 30666,
"flags": [
- "--rpc-port=9865",
"-lparachain::candidate_validation=debug"
]
},
@@ -39,7 +36,6 @@
"rpcPort": 9876,
"port": 30777,
"flags": [
- "--rpc-port=9876",
"-lparachain::candidate_validation=debug"
]
}
@@ -70,7 +66,6 @@
"name": "alice",
"flags": [
"--rpc-cors=all",
- "--rpc-port=9933",
"--unsafe-rpc-external",
"--unsafe-ws-external"
]
@@ -82,7 +77,6 @@
"name": "bob",
"flags": [
"--rpc-cors=all",
- "--rpc-port=9934",
"--unsafe-rpc-external",
"--unsafe-ws-external"
]
pallets/common/Cargo.tomldiffbeforeafterboth--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -15,10 +15,10 @@
sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
+up-evm-mapping = { default-features = false, path = '../../primitives/evm-mapping' }
nft-data-structs = { default-features = false, path = '../../primitives/nft' }
pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
evm-coder = { default-features = false, path = '../../crates/evm-coder' }
-
pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
serde = { version = "1.0.130", default-features = false }
scale-info = { version = "1.0.0", default-features = false, features = [
@@ -32,6 +32,7 @@
"frame-system/std",
"sp-runtime/std",
"sp-std/std",
+ "up-evm-mapping/std",
"nft-data-structs/std",
"pallet-evm/std",
]
pallets/common/src/account.rsdiffbeforeafterboth--- a/pallets/common/src/account.rs
+++ b/pallets/common/src/account.rs
@@ -2,12 +2,12 @@
use codec::{Encode, EncodeLike, Decode};
use sp_core::H160;
use scale_info::{Type, TypeInfo};
-use sp_core::crypto::AccountId32;
use core::cmp::Ordering;
use serde::{Serialize, Deserialize};
use pallet_evm::AddressMapping;
use sp_std::vec::Vec;
use sp_std::clone::Clone;
+use up_evm_mapping::EvmBackwardsAddressMapping;
pub trait CrossAccountId<AccountId>:
Encode + EncodeLike + Decode + TypeInfo + Clone + PartialEq + Ord + core::fmt::Debug + Default
@@ -174,19 +174,5 @@
} else {
BasicCrossAccountIdRepr::Substrate(v.as_sub().clone())
}
- }
-}
-
-pub trait EvmBackwardsAddressMapping<AccountId> {
- fn from_account_id(account_id: AccountId) -> H160;
-}
-
-/// Should have same mapping as EnsureAddressTruncated
-pub struct MapBackwardsAddressTruncated;
-impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
- fn from_account_id(account_id: AccountId32) -> H160 {
- let mut out = [0; 20];
- out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
- H160(out)
}
}
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -141,7 +141,7 @@
pub mod pallet {
use super::*;
use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};
- use account::{EvmBackwardsAddressMapping, CrossAccountId};
+ use account::CrossAccountId;
use frame_support::traits::Currency;
use nft_data_structs::TokenId;
use scale_info::TypeInfo;
@@ -153,7 +153,7 @@
type CrossAccountId: CrossAccountId<Self::AccountId>;
type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
- type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
+ type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;
type Currency: Currency<Self::AccountId>;
type CollectionCreationPrice: Get<
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -59,7 +59,8 @@
fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
self.0.consume_sload()?;
- Ok(<Pallet<T>>::allowed(contract_address, user, true))
+ Ok(<Pallet<T>>::allowed(contract_address, user)
+ || !<AllowlistEnabled<T>>::get(contract_address))
}
fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
@@ -113,7 +114,7 @@
value: sp_core::U256,
) -> Option<PrecompileOutput> {
// TODO: Extract to another OnMethodCall handler
- if !<Pallet<T>>::allowed(*target, *source, true) {
+ if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {
return Some(PrecompileOutput {
exit_status: ExitReason::Revert(ExitRevert::Reverted),
cost: 0,
@@ -151,22 +152,26 @@
pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
- if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
- let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);
- let limit_time = last_tx_block + rate_limit;
+ if !<SelfSponsoring<T>>::get(&call.0) {
+ return None;
+ }
+ if !<Pallet<T>>::allowed(call.0, *who) {
+ return None;
+ }
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- if block_number > limit_time {
- <SponsorBasket<T>>::insert(&call.0, who, block_number);
- return Some(call.0);
- }
- } else {
- <SponsorBasket<T>>::insert(&call.0, who, block_number);
- return Some(call.0);
+ if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
+ let limit = <SponsoringRateLimit<T>>::get(&call.0);
+
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
- None
+
+ <SponsorBasket<T>>::insert(&call.0, who, block_number);
+
+ Some(call.0)
}
}
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -76,11 +76,7 @@
<SponsoringRateLimit<T>>::insert(contract, rate_limit);
}
- /// Default is returned if allowlist is disabled
- pub fn allowed(contract: H160, user: H160, default: bool) -> bool {
- if !<AllowlistEnabled<T>>::get(contract) {
- return default;
- }
+ pub fn allowed(contract: H160, user: H160) -> bool {
<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
}
pallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth--- a/pallets/evm-transaction-payment/Cargo.toml
+++ b/pallets/evm-transaction-payment/Cargo.toml
@@ -15,6 +15,7 @@
fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
+up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
[dependencies.codec]
default-features = false
@@ -35,4 +36,5 @@
"pallet-ethereum/std",
"fp-evm/std",
"up-sponsorship/std",
+ "up-evm-mapping/std",
]
pallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-transaction-payment/src/lib.rs
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -1,98 +1,140 @@
#![cfg_attr(not(feature = "std"), no_std)]
+use core::marker::PhantomData;
+use fp_evm::WithdrawReason;
+use frame_support::traits::{Currency, IsSubType};
pub use pallet::*;
+use pallet_evm::{EVMCurrencyAdapter, EnsureAddressOrigin};
+use sp_core::{H160, U256};
+use sp_runtime::TransactionOutcome;
+use up_sponsorship::SponsorshipHandler;
+use up_evm_mapping::EvmBackwardsAddressMapping;
+use pallet_evm::AddressMapping;
#[frame_support::pallet]
pub mod pallet {
- use core::marker::PhantomData;
+ use super::*;
+
use frame_support::traits::Currency;
- use pallet_evm::EVMCurrencyAdapter;
- use fp_evm::WithdrawReason;
- use sp_core::{H160, U256};
- use sp_runtime::TransactionOutcome;
- use up_sponsorship::SponsorshipHandler;
use sp_std::vec::Vec;
- type NegativeImbalanceOf<C, T> =
- <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
-
#[pallet::config]
pub trait Config: frame_system::Config {
- type SponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
+ type EvmSponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
type Currency: Currency<Self::AccountId>;
+ type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
+ type EvmAddressMapping: AddressMapping<Self::AccountId>;
}
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
+}
- pub struct ChargeEvmLiquidityInfo<T>
- where
- T: Config,
- T: pallet_evm::Config,
- {
- who: H160,
- negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,
- }
+type NegativeImbalanceOf<C, T> =
+ <C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
- pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
- impl<T: Config> fp_evm::TransactionValidityHack for TransactionValidityHack<T> {
- fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option<H160> {
- match reason {
- WithdrawReason::Call { target, input } => {
- // This method is only used for checking, we shouldn't touch storage in it
- frame_support::storage::with_transaction(|| {
- TransactionOutcome::Rollback(T::SponsorshipHandler::get_sponsor(
- &origin,
- &(*target, input.clone()),
- ))
- })
- }
- _ => None,
+pub struct ChargeEvmLiquidityInfo<T>
+where
+ T: Config,
+ T: pallet_evm::Config,
+{
+ who: H160,
+ negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,
+}
+
+pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
+impl<T: Config> fp_evm::TransactionValidityHack for TransactionValidityHack<T> {
+ fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option<H160> {
+ match reason {
+ WithdrawReason::Call { target, input } => {
+ // This method is only used for checking, we shouldn't touch storage in it
+ frame_support::storage::with_transaction(|| {
+ TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
+ &origin,
+ &(*target, input.clone()),
+ ))
+ })
}
+ _ => None,
+ }
+ }
+}
+pub struct OnChargeTransaction<T: Config>(PhantomData<*const T>);
+impl<T> pallet_evm::OnChargeEVMTransaction<T> for OnChargeTransaction<T>
+where
+ T: Config,
+ T: pallet_evm::Config,
+{
+ type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;
+
+ fn withdraw_fee(
+ who: &H160,
+ reason: WithdrawReason,
+ fee: U256,
+ ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
+ let mut who_pays_fee = *who;
+ if let WithdrawReason::Call { target, input } = &reason {
+ who_pays_fee = T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
+ .unwrap_or(who_pays_fee);
}
+ let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
+ &who_pays_fee,
+ reason,
+ fee,
+ )?;
+ Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {
+ who: who_pays_fee,
+ negative_imbalance: i,
+ }))
}
- pub struct OnChargeTransaction<T: Config>(PhantomData<*const T>);
- impl<T> pallet_evm::OnChargeEVMTransaction<T> for OnChargeTransaction<T>
- where
- T: Config,
- T: pallet_evm::Config,
- {
- type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;
+ fn correct_and_deposit_fee(
+ who: &H160,
+ corrected_fee: U256,
+ already_withdrawn: Self::LiquidityInfo,
+ ) {
+ <EVMCurrencyAdapter<<T as Config>::Currency, ()> as pallet_evm::OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
+ &already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),
+ corrected_fee,
+ already_withdrawn.map(|e| e.negative_imbalance),
+ )
+ }
+}
- fn withdraw_fee(
- who: &H160,
- reason: WithdrawReason,
- fee: U256,
- ) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
- let mut who_pays_fee = *who;
- if let WithdrawReason::Call { target, input } = &reason {
- who_pays_fee = T::SponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
- .unwrap_or(who_pays_fee);
+/// Implements sponsoring for evm calls performed from pallet-evm (via api.tx.ethereum.transact/api.tx.evm.call)
+pub struct BridgeSponsorshipHandler<T>(PhantomData<T>);
+impl<T, C> SponsorshipHandler<T::AccountId, C> for BridgeSponsorshipHandler<T>
+where
+ T: Config + pallet_evm::Config,
+ C: IsSubType<pallet_evm::Call<T>>,
+{
+ fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+ match call.is_sub_type()? {
+ pallet_evm::Call::call {
+ source,
+ target,
+ input,
+ ..
+ } => {
+ let _ = T::CallOrigin::ensure_address_origin(
+ source,
+ <frame_system::RawOrigin<T::AccountId>>::Signed(who.clone()).into(),
+ )
+ .ok()?;
+ let who = T::EvmBackwardsAddressMapping::from_account_id(who.clone());
+ // Effects from EvmSponsorshipHandler are applied in OnChargeEvmTransaction by pallet_evm::runner
+ // TODO: Should we implement simulation mode (test, but do not apply effects) in `up-sponsorship`?
+ let sponsor = frame_support::storage::with_transaction(|| {
+ TransactionOutcome::Rollback(T::EvmSponsorshipHandler::get_sponsor(
+ &who,
+ &(target.clone(), input.clone()),
+ ))
+ })?;
+ let sponsor = T::EvmAddressMapping::into_account_id(sponsor);
+ Some(sponsor)
}
- let negative_imbalance =
- EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
- &who_pays_fee,
- reason,
- fee,
- )?;
- Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {
- who: who_pays_fee,
- negative_imbalance: i,
- }))
- }
-
- fn correct_and_deposit_fee(
- who: &H160,
- corrected_fee: U256,
- already_withdrawn: Self::LiquidityInfo,
- ) {
- <EVMCurrencyAdapter<<T as Config>::Currency, ()> as pallet_evm::OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
- &already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),
- corrected_fee,
- already_withdrawn.map(|e| e.negative_imbalance),
- )
+ _ => None,
}
}
}
pallets/nft-transaction-payment/Cargo.tomldiffbeforeafterboth--- a/pallets/nft-transaction-payment/Cargo.toml
+++ /dev/null
@@ -1,51 +0,0 @@
-[package]
-authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
-description = 'Unqiue pallet nft specific transaction payment'
-edition = '2018'
-homepage = 'https://substrate.io'
-license = 'Unlicense'
-name = 'pallet-nft-transaction-payment'
-repository = 'https://github.com/usetech-llc/nft_private/'
-version = '3.0.0'
-
-[package.metadata.docs.rs]
-targets = ['x86_64-unknown-linux-gnu']
-
-# alias "parity-scale-code" to "codec"
-[dependencies.codec]
-default-features = false
-features = ['derive']
-package = 'parity-scale-codec'
-version = '2.3.0'
-
-[dependencies]
-scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
-serde = { version = "1.0.130", default-features = false }
-frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-pallet-transaction-payment = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-sp-io = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-
-up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
-
-[features]
-default = ['std']
-std = [
- 'codec/std',
- 'serde/std',
- 'frame-support/std',
- 'frame-system/std',
- 'sp-core/std',
- 'sp-io/std',
- 'pallet-transaction-payment/std',
- 'sp-std/std',
- 'sp-runtime/std',
- 'frame-benchmarking/std',
-
- 'up-sponsorship/std',
-]
-runtime-benchmarks = ["frame-benchmarking"]
pallets/nft-transaction-payment/README.mddiffbeforeafterboth--- a/pallets/nft-transaction-payment/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Nft Transaction Payment
-
-## Overview
-
-A module containing the sponsoring logic for paying for sponsored collections
-
-**NOTE:** The scheduled calls will be dispatched with the default filter
-for the origin: namely `frame_system::Config::BaseCallFilter` for all origin
-except root which will get no filter. And not the filter contained in origin
-use to call `fn schedule`.
-
-If a call is scheduled using proxy or whatever mecanism which adds filter,
-then those filter will not be used when dispatching the schedule call.
pallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth--- a/pallets/nft-transaction-payment/src/lib.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// This file is subject to the terms and conditions defined in
-// file 'LICENSE', which is part of this source code package.
-//
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-#[cfg(feature = "std")]
-pub use std::*;
-
-#[cfg(feature = "std")]
-pub use serde::*;
-
-use frame_support::{decl_module, decl_storage};
-use sp_std::prelude::*;
-use up_sponsorship::SponsorshipHandler;
-
-pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
- type SponsorshipHandler: SponsorshipHandler<Self::AccountId, Self::Call>;
-}
-
-decl_storage! {
- trait Store for Module<T: Config> as NftTransactionPayment{
- }
-}
-
-decl_module! {
- pub struct Module<T: Config> for enum Call
- where
- origin: T::Origin,
- {
- }
-}
-
-impl<T: Config> Module<T> {
- pub fn withdraw_type(who: &T::AccountId, call: &T::Call) -> Option<T::AccountId> {
- T::SponsorshipHandler::get_sponsor(who, call)
- }
-}
pallets/nft/Cargo.tomldiffbeforeafterboth--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -34,6 +34,7 @@
'fp-evm/std',
'nft-data-structs/std',
'up-sponsorship/std',
+ 'up-evm-mapping/std',
'sp-std/std',
'sp-api/std',
'sp-runtime/std',
@@ -135,7 +136,7 @@
sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.12" }
up-sponsorship = { version = "0.1.0", default-features = false, git = "https://github.com/UniqueNetwork/pallet-sponsoring" }
-
+up-evm-mapping = { default-features = false, path = "../../primitives/evm-mapping" }
evm-coder = { default-features = false, path = "../../crates/evm-coder" }
pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
primitive-types = { version = "0.10.1", default-features = false, features = [
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -1,123 +1,64 @@
//! Implements EVM sponsoring logic via OnChargeEVMTransaction
-use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};
+use crate::{Config, sponsorship::*};
use evm_coder::{Call, abi::AbiReader};
-use frame_support::{
- storage::{StorageDoubleMap},
-};
-use pallet_common::eth::map_eth_to_id;
+use pallet_common::{CollectionHandle, eth::map_eth_to_id};
use sp_core::H160;
use sp_std::prelude::*;
use up_sponsorship::SponsorshipHandler;
use core::marker::PhantomData;
use core::convert::TryInto;
-use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
-use pallet_common::{
- CollectionById,
- account::{CrossAccountId, EvmBackwardsAddressMapping},
-};
+use nft_data_structs::TokenId;
+use up_evm_mapping::EvmBackwardsAddressMapping;
+use pallet_evm::AddressMapping;
use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
-struct AnyError;
-
-fn try_sponsor<T: Config>(
- caller: &H160,
- collection_id: CollectionId,
- collection: &Collection<T::AccountId>,
- call: &[u8],
-) -> Result<(), AnyError> {
- let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
- match &collection.mode {
- crate::CollectionMode::NFT => {
- let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)
- .map_err(|_| AnyError)?
- .ok_or(AnyError)?;
- match call {
- UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {
- token_id,
- ..
- })
- | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
- let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let collection_limits = &collection.limits;
- let limit =
- collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
-
- let mut sponsor = true;
- if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {
- let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsor = false;
- }
+pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
+impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
+ fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
+ let collection_id = map_eth_to_id(&call.0)?;
+ let collection = <CollectionHandle<T>>::new(collection_id)?;
+ let sponsor = collection.sponsorship.sponsor()?.clone();
+ let sponsor =
+ <T as pallet_common::Config>::EvmBackwardsAddressMapping::from_account_id(sponsor);
+ let who = <T as pallet_common::Config>::EvmAddressMapping::into_account_id(*who);
+ let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;
+ match &collection.mode {
+ crate::CollectionMode::NFT => {
+ let call = UniqueNFTCall::parse(method_id, &mut reader).ok()??;
+ match call {
+ UniqueNFTCall::ERC721UniqueExtensions(
+ ERC721UniqueExtensionsCall::Transfer { token_id, .. },
+ )
+ | UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
}
- if sponsor {
- <NftTransferBasket<T>>::insert(collection_id, token_id, block_number);
- return Ok(());
+ UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {
+ let token_id: TokenId = token_id.try_into().ok()?;
+ withdraw_approve::<T>(&collection, &who, &token_id).map(|()| sponsor)
}
+ _ => None,
}
- _ => {}
}
- }
- crate::CollectionMode::Fungible(_) => {
- let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)
- .map_err(|_| AnyError)?
- .ok_or(AnyError)?;
- #[allow(clippy::single_match)]
- match call {
- UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
- let who = T::CrossAccountId::from_eth(*caller);
- let collection_limits = &collection.limits;
- let limit = collection_limits
- .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
-
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let mut sponsored = true;
- if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {
- let last_tx_block =
- <FungibleTransferBasket<T>>::get(collection_id, who.as_sub());
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- <FungibleTransferBasket<T>>::insert(
- collection_id,
- who.as_sub(),
- block_number,
- );
- return Ok(());
+ crate::CollectionMode::Fungible(_) => {
+ let call = UniqueFungibleCall::parse(method_id, &mut reader).ok()??;
+ #[allow(clippy::single_match)]
+ match call {
+ UniqueFungibleCall::ERC20(
+ ERC20Call::Transfer { .. } | ERC20Call::TransferFrom { .. },
+ ) => withdraw_transfer::<T>(&collection, &who, &TokenId::default())
+ .map(|()| sponsor),
+ UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {
+ withdraw_approve::<T>(&collection, &who, &TokenId::default())
+ .map(|()| sponsor)
}
- }
- _ => {}
- }
- }
- _ => {}
- }
- Err(AnyError)
-}
-
-pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {
- fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
- if let Some(collection_id) = map_eth_to_id(&call.0) {
- if let Some(collection) = <CollectionById<T>>::get(collection_id) {
- if !collection.sponsorship.confirmed() {
- return None;
- }
- if try_sponsor::<T>(who, collection_id, &collection, &call.1).is_ok() {
- return collection
- .sponsorship
- .sponsor()
- .cloned()
- .map(T::EvmBackwardsAddressMapping::from_account_id);
+ _ => None,
}
}
+ _ => None,
}
- None
}
}
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -136,18 +136,22 @@
//#region Tokens transfer rate limit baskets
/// (Collection id (controlled?2), who created (real))
/// TODO: Off chain worker should remove from this map when collection gets removed
- pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;
+ pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;
/// Collection id (controlled?2), token id (controlled?2)
- pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;
+ pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
/// Collection id (controlled?2), owning user (real)
- pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;
+ pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
/// Collection id (controlled?2), token id (controlled?2)
- pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;
+ 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>;
//#endregion
/// Variable metadata sponsoring
/// Collection id (controlled?2), token id (controlled?2)
- pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;
+ pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+ /// Approval sponsoring
+ pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+ pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
+ 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>;
}
}
@@ -249,9 +253,12 @@
<NftTransferBasket<T>>::remove_prefix(collection_id, None);
<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
- <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);
+ <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);
<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
+ <NftApproveBasket<T>>::remove_prefix(collection_id, None);
+ <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);
+ <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);
Ok(())
}
@@ -592,7 +599,15 @@
pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))
+ let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;
+ if value == 1 {
+ <NftTransferBasket<T>>::remove(collection_id, item_id);
+ <NftApproveBasket<T>>::remove(collection_id, item_id);
+ }
+ // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?
+ // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());
+ // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));
+ Ok(post_info)
}
/// Destroys a concrete instance of NFT on behalf of the owner
pallets/nft/src/sponsorship.rsdiffbeforeafterboth--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -1,175 +1,189 @@
use crate::{
Config, Call, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket,
- FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode,
+ FungibleTransferBasket, NftTransferBasket, CreateItemData, CollectionMode, NftApproveBasket,
+ FungibleApproveBasket, RefungibleApproveBasket,
};
use core::marker::PhantomData;
use up_sponsorship::SponsorshipHandler;
use frame_support::{
traits::{IsSubType},
- storage::{StorageMap, StorageDoubleMap},
+ storage::{StorageMap, StorageDoubleMap, StorageNMap},
};
use nft_data_structs::{
- TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionId, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, NFT_SPONSOR_TRANSFER_TIMEOUT,
+ REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, TokenId,
};
-use pallet_common::{CollectionById};
+use pallet_common::{CollectionHandle};
+use pallet_common::account::CrossAccountId;
-pub struct NftSponsorshipHandler<T>(PhantomData<T>);
-impl<T: Config> NftSponsorshipHandler<T> {
- pub fn withdraw_create_item(
- who: &T::AccountId,
- collection_id: &CollectionId,
- _properties: &CreateItemData,
- ) -> Option<T::AccountId> {
- let collection = CollectionById::<T>::get(collection_id)?;
+pub fn withdraw_transfer<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // preliminary sponsoring correctness check
+ if !((pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner).as_sub() == who)
+ || (pallet_refungible::Owned::<T>::get((
+ collection.id,
+ T::CrossAccountId::from_sub(who.clone()),
+ item_id,
+ ))) {
+ return None;
+ }
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match collection.mode {
+ CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
- let limit = collection
- .limits
- .sponsor_transfer_timeout(match _properties {
- CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- });
- if CreateItemBasket::<T>::contains_key((collection_id, &who)) {
- let last_tx_block = CreateItemBasket::<T>::get((collection_id, &who));
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- return None;
- }
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => <FungibleTransferBasket<T>>::get(collection.id, who),
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::get((collection.id, item_id, who))
}
- CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
+ };
- // check free create limit
- if collection.limits.sponsored_data_size() >= (_properties.data_size() as u32) {
- collection.sponsorship.sponsor().cloned()
- } else {
- None
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
- pub fn withdraw_transfer(
- who: &T::AccountId,
- collection_id: &CollectionId,
- item_id: &TokenId,
- ) -> Option<T::AccountId> {
- let collection = CollectionById::<T>::get(collection_id)?;
+ match collection.mode {
+ CollectionMode::NFT => <NftTransferBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleTransferBasket<T>>::insert(collection.id, who, block_number)
+ }
+ CollectionMode::ReFungible => {
+ <ReFungibleTransferBasket<T>>::insert((collection.id, item_id, who), block_number)
+ }
+ };
- let mut sponsor_transfer = false;
- if collection.sponsorship.confirmed() {
- let collection_limits = collection.limits.clone();
- let collection_mode = collection.mode.clone();
+ Some(())
+}
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- sponsor_transfer = match collection_mode {
- CollectionMode::NFT => {
- // get correct limit
- let limit =
- collection_limits.sponsor_transfer_timeout(NFT_SPONSOR_TRANSFER_TIMEOUT);
+pub fn withdraw_create_item<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ _properties: &CreateItemData,
+) -> Option<()> {
+ if _properties.data_size() as u32 > collection.limits.sponsored_data_size() {
+ return None;
+ }
- let mut sponsored = true;
- if NftTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block = NftTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection
+ .limits
+ .sponsor_transfer_timeout(match _properties {
+ CreateItemData::NFT(_) => NFT_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ CreateItemData::ReFungible(_) => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ });
- sponsored
- }
- CollectionMode::Fungible(_) => {
- // get correct limit
- let limit = collection_limits
- .sponsor_transfer_timeout(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
+ if let Some(last_tx_block) = <CreateItemBasket<T>>::get((collection.id, &who)) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let mut sponsored = true;
- if FungibleTransferBasket::<T>::contains_key(collection_id, who) {
- let last_tx_block = FungibleTransferBasket::<T>::get(collection_id, who);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
- }
+ CreateItemBasket::<T>::insert((collection.id, who.clone()), block_number);
- sponsored
- }
- CollectionMode::ReFungible => {
- // get correct limit
- let limit = collection_limits
- .sponsor_transfer_timeout(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT);
+ Some(())
+}
- let mut sponsored = true;
- if ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block =
- ReFungibleTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
+pub fn withdraw_set_variable_meta_data<T: Config>(
+ who: &T::AccountId,
+ collection: &CollectionHandle<T>,
+ item_id: &TokenId,
+ data: &[u8],
+) -> Option<()> {
+ // preliminary sponsoring correctness check
+ if !((pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner).as_sub() == who)
+ || (pallet_refungible::Owned::<T>::get((
+ collection.id,
+ T::CrossAccountId::from_sub(who.clone()),
+ item_id,
+ ))) {
+ return None;
+ }
- sponsored
- }
- };
- }
+ // Can't sponsor fungible collection, this tx will be rejected
+ // as invalid
+ if matches!(collection.mode, CollectionMode::Fungible(_)) {
+ return None;
+ }
+ if data.len() > collection.limits.sponsored_data_size() as usize {
+ return None;
+ }
- if !sponsor_transfer {
- None
- } else {
- collection.sponsorship.sponsor().cloned()
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsored_data_rate_limit()?;
+
+ if let Some(last_tx_block) = VariableMetaDataBasket::<T>::get(collection.id, item_id) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
- pub fn withdraw_set_variable_meta_data(
- collection_id: &CollectionId,
- item_id: &TokenId,
- data: &[u8],
- ) -> Option<T::AccountId> {
- let mut sponsor_metadata_changes = false;
+ <VariableMetaDataBasket<T>>::insert(collection.id, item_id, block_number);
- let collection = CollectionById::<T>::get(collection_id)?;
+ Some(())
+}
- if collection.sponsorship.confirmed() &&
- // Can't sponsor fungible collection, this tx will be rejected
- // as invalid
- !matches!(collection.mode, CollectionMode::Fungible(_)) &&
- data.len() <= collection.limits.sponsored_data_size() as usize
- {
- if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit() {
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+pub fn withdraw_approve<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::AccountId,
+ item_id: &TokenId,
+) -> Option<()> {
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsor_approve_timeout();
- if VariableMetaDataBasket::<T>::get(collection_id, item_id)
- .map(|last_block| block_number - last_block > rate_limit.into())
- .unwrap_or(true)
- {
- sponsor_metadata_changes = true;
- VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
- }
- }
+ let last_tx_block = match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::get(collection.id, item_id),
+ CollectionMode::Fungible(_) => <FungibleApproveBasket<T>>::get(collection.id, who),
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::get((collection.id, item_id, who))
}
+ };
- if !sponsor_metadata_changes {
- None
- } else {
- collection.sponsorship.sponsor().cloned()
+ if let Some(last_tx_block) = last_tx_block {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
}
}
+
+ match collection.mode {
+ CollectionMode::NFT => <NftApproveBasket<T>>::insert(collection.id, item_id, block_number),
+ CollectionMode::Fungible(_) => {
+ <FungibleApproveBasket<T>>::insert(collection.id, who, block_number)
+ }
+ CollectionMode::ReFungible => {
+ <RefungibleApproveBasket<T>>::insert((collection.id, item_id, who), block_number)
+ }
+ };
+
+ Some(())
+}
+
+fn load<T: Config>(id: CollectionId) -> Option<(T::AccountId, CollectionHandle<T>)> {
+ let collection = CollectionHandle::new(id)?;
+ let sponsor = collection.sponsorship.sponsor().cloned()?;
+ Some((sponsor, collection))
}
+pub struct NftSponsorshipHandler<T>(PhantomData<T>);
impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
where
T: Config,
@@ -181,17 +195,40 @@
collection_id,
data,
..
- } => Self::withdraw_create_item(who, collection_id, data),
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_create_item::<T>(&collection, who, data).map(|()| sponsor)
+ }
Call::transfer {
collection_id,
item_id,
..
- } => Self::withdraw_transfer(who, collection_id, item_id),
+ }
+ | Call::transfer_from {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_transfer::<T>(&collection, who, item_id).map(|()| sponsor)
+ }
+ Call::approve {
+ collection_id,
+ item_id,
+ ..
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_approve::<T>(&collection, who, item_id).map(|()| sponsor)
+ }
Call::set_variable_meta_data {
collection_id,
item_id,
data,
- } => Self::withdraw_set_variable_meta_data(collection_id, item_id, data),
+ } => {
+ let (sponsor, collection) = load(*collection_id)?;
+ withdraw_set_variable_meta_data::<T>(&who, &collection, item_id, data)
+ .map(|()| sponsor)
+ }
_ => None,
}
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -58,18 +58,18 @@
}
#[pallet::pallet]
- #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::generate_store(pub trait Store)]
pub struct Pallet<T>(_);
#[pallet::storage]
- pub(super) type TokensMinted<T: Config> =
+ pub type TokensMinted<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]
- pub(super) type TokensBurnt<T: Config> =
+ pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]
- pub(super) type TokenData<T: Config> = StorageNMap<
+ pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = ItemData<T>,
QueryKind = OptionQuery,
@@ -77,7 +77,7 @@
/// Used to enumerate tokens owned by account
#[pallet::storage]
- pub(super) type Owned<T: Config> = StorageNMap<
+ pub type Owned<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Blake2_128Concat, T::CrossAccountId>,
@@ -88,7 +88,7 @@
>;
#[pallet::storage]
- pub(super) type AccountBalance<T: Config> = StorageNMap<
+ pub type AccountBalance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Blake2_128Concat, T::CrossAccountId>,
@@ -98,7 +98,7 @@
>;
#[pallet::storage]
- pub(super) type Allowance<T: Config> = StorageNMap<
+ pub type Allowance<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = T::CrossAccountId,
QueryKind = OptionQuery,
@@ -328,7 +328,7 @@
.checked_add(data.len() as u32)
.ok_or(ArithmeticError::Overflow)?;
ensure!(
- tokens_minted < collection.limits.token_limit(),
+ tokens_minted <= collection.limits.token_limit(),
<CommonError<T>>::CollectionTokenLimitExceeded
);
collection.consume_sstore()?;
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -54,25 +54,25 @@
}
#[pallet::pallet]
- #[pallet::generate_store(pub(super) trait Store)]
+ #[pallet::generate_store(pub trait Store)]
pub struct Pallet<T>(_);
#[pallet::storage]
- pub(super) type TokensMinted<T: Config> =
+ pub type TokensMinted<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]
- pub(super) type TokensBurnt<T: Config> =
+ pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
#[pallet::storage]
- pub(super) type TokenData<T: Config> = StorageNMap<
+ pub type TokenData<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = ItemData,
QueryKind = ValueQuery,
>;
#[pallet::storage]
- pub(super) type TotalSupply<T: Config> = StorageNMap<
+ pub type TotalSupply<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = u128,
QueryKind = ValueQuery,
@@ -80,7 +80,7 @@
/// Used to enumerate tokens owned by account
#[pallet::storage]
- pub(super) type Owned<T: Config> = StorageNMap<
+ pub type Owned<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Blake2_128Concat, T::CrossAccountId>,
@@ -91,7 +91,7 @@
>;
#[pallet::storage]
- pub(super) type AccountBalance<T: Config> = StorageNMap<
+ pub type AccountBalance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
// Owner
@@ -102,7 +102,7 @@
>;
#[pallet::storage]
- pub(super) type Balance<T: Config> = StorageNMap<
+ pub type Balance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Twox64Concat, TokenId>,
@@ -114,7 +114,7 @@
>;
#[pallet::storage]
- pub(super) type Allowance<T: Config> = StorageNMap<
+ pub type Allowance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Twox64Concat, TokenId>,
primitives/evm-mapping/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/primitives/evm-mapping/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "up-evm-mapping"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
+frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
+
+[features]
+default = ["std"]
+std = [
+ "sp-core/std",
+ "frame-support/std",
+]
primitives/evm-mapping/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/primitives/evm-mapping/src/lib.rs
@@ -0,0 +1,23 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use frame_support::sp_runtime::AccountId32;
+use sp_core::H160;
+
+/// Transforms substrate addresses to ethereum (Reverse of `EvmAddressMapping`)
+/// pallet_evm doesn't have this, as it only checks if eth address
+/// is owned by substrate via `EnsureAddressOrigin` trait
+///
+/// This trait implementations shouldn't conflict with used `EnsureAddressOrigin`
+pub trait EvmBackwardsAddressMapping<AccountId> {
+ fn from_account_id(account_id: AccountId) -> H160;
+}
+
+/// Should have same mapping as EnsureAddressTruncated
+pub struct MapBackwardsAddressTruncated;
+impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
+ fn from_account_id(account_id: AccountId32) -> H160 {
+ let mut out = [0; 20];
+ out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
+ H160(out)
+ }
+}
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -54,6 +54,8 @@
pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;
+
// Schema limits
pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;
pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
@@ -246,6 +248,7 @@
pub variable_data: Vec<u8>,
}
+/// All fields are wrapped in `Option`s, where None means chain default
#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct CollectionLimits {
@@ -254,11 +257,12 @@
/// None - setVariableMetadata is not sponsored
/// Some(v) - setVariableMetadata is sponsored
/// if there is v block between txs
- pub sponsored_data_rate_limit: Option<u32>,
+ pub sponsored_data_rate_limit: Option<(Option<u32>,)>,
pub token_limit: Option<u32>,
// Timeouts for item types in passed blocks
pub sponsor_transfer_timeout: Option<u32>,
+ pub sponsor_approve_timeout: Option<u32>,
pub owner_can_transfer: Option<bool>,
pub owner_can_destroy: Option<bool>,
pub transfers_enabled: Option<bool>,
@@ -285,6 +289,11 @@
.unwrap_or(default)
.min(MAX_SPONSOR_TIMEOUT)
}
+ pub fn sponsor_approve_timeout(&self) -> u32 {
+ self.sponsor_approve_timeout
+ .unwrap_or(SPONSOR_APPROVE_TIMEOUT)
+ .min(MAX_SPONSOR_TIMEOUT)
+ }
pub fn owner_can_transfer(&self) -> bool {
self.owner_can_transfer.unwrap_or(true)
}
@@ -296,6 +305,8 @@
}
pub fn sponsored_data_rate_limit(&self) -> Option<u32> {
self.sponsored_data_rate_limit
+ .unwrap_or((None,))
+ .0
.map(|v| v.min(MAX_SPONSOR_TIMEOUT))
}
}
runtime/Cargo.tomldiffbeforeafterboth--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -60,7 +60,7 @@
'pallet-transaction-payment/std',
'pallet-transaction-payment-rpc-runtime-api/std',
'pallet-treasury/std',
- 'pallet-vesting/std',
+ # 'pallet-vesting/std',
'pallet-evm/std',
'pallet-evm-migration/std',
'pallet-evm-contract-helpers/std',
@@ -69,6 +69,7 @@
'pallet-ethereum/std',
'fp-rpc/std',
'up-rpc/std',
+ 'up-evm-mapping/std',
'fp-self-contained/std',
'parachain-info/std',
'serde',
@@ -80,7 +81,6 @@
'pallet-nft/std',
'pallet-unq-scheduler/std',
'pallet-nft-charge-transaction/std',
- 'pallet-nft-transaction-payment/std',
'nft-data-structs/std',
'sp-api/std',
'sp-block-builder/std',
@@ -97,6 +97,8 @@
'xcm/std',
'xcm-builder/std',
'xcm-executor/std',
+
+ "orml-vesting/std",
]
limit-testing = ['pallet-nft/limit-testing', 'nft-data-structs/limit-testing']
@@ -210,10 +212,10 @@
git = 'https://github.com/paritytech/substrate.git'
branch = 'polkadot-v0.9.12'
-[dependencies.pallet-vesting]
-default-features = false
-git = 'https://github.com/paritytech/substrate.git'
-branch = 'polkadot-v0.9.12'
+# [dependencies.pallet-vesting]
+# default-features = false
+# git = 'https://github.com/paritytech/substrate.git'
+# branch = 'polkadot-v0.9.12'
[dependencies.sp-arithmetic]
default-features = false
@@ -359,6 +361,10 @@
branch = 'release-v0.9.12'
default-features = false
+[dependencies.orml-vesting]
+git = 'https://github.com/UniqueNetwork/open-runtime-module-library'
+version = "0.4.1-dev"
+default-features = false
################################################################################
# local dependencies
@@ -370,6 +376,7 @@
derivative = "2.2.0"
pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
up-rpc = { path = "../primitives/rpc", default-features = false }
+up-evm-mapping = { path = "../primitives/evm-mapping", default-features = false }
pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
pallet-common = { default-features = false, path = "../pallets/common" }
@@ -378,8 +385,7 @@
pallet-nonfungible = { default-features = false, path = "../pallets/nonfungible" }
pallet-unq-scheduler = { path = '../pallets/scheduler', default-features = false, version = '3.0.0' }
# pallet-contract-helpers = { path = '../pallets/contract-helpers', default-features = false, version = '0.1.0' }
-pallet-nft-transaction-payment = { path = '../pallets/nft-transaction-payment', default-features = false, version = '3.0.0' }
-pallet-nft-charge-transaction = {git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }
+pallet-nft-charge-transaction = { git = "https://github.com/UniqueNetwork/pallet-sponsoring", package = "pallet-template-transaction-payment", default-features = false, version = '3.0.0' }
pallet-evm-migration = { path = '../pallets/evm-migration', default-features = false }
pallet-evm-contract-helpers = { path = '../pallets/evm-contract-helpers', default-features = false }
pallet-evm-transaction-payment = { path = '../pallets/evm-transaction-payment', default-features = false }
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -22,8 +22,7 @@
use sp_runtime::{
Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,
traits::{
- AccountIdLookup, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,
- AccountIdConversion,
+ AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,
},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, MultiSignature,
@@ -70,7 +69,7 @@
use fp_rpc::TransactionStatus;
use sp_core::crypto::Public;
use sp_runtime::{
- traits::{Dispatchable, PostDispatchInfoOf},
+ traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},
transaction_validity::TransactionValidityError,
};
@@ -515,17 +514,33 @@
type Call = Call;
}
+pub struct RelayChainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);
+
+impl<T: cumulus_pallet_parachain_system::Config> BlockNumberProvider
+ for RelayChainBlockNumberProvider<T>
+{
+ type BlockNumber = BlockNumber;
+
+ fn current_block_number() -> Self::BlockNumber {
+ cumulus_pallet_parachain_system::Pallet::<T>::validation_data()
+ .map(|d| d.relay_parent_number)
+ .unwrap_or_default()
+ }
+}
+
parameter_types! {
pub const MinVestedTransfer: Balance = 10 * UNIQUE;
+ pub const MaxVestingSchedules: u32 = 28;
}
-impl pallet_vesting::Config for Runtime {
+impl orml_vesting::Config for Runtime {
type Event = Event;
- type Currency = Balances;
- type BlockNumberToBalance = ConvertInto;
+ type Currency = pallet_balances::Pallet<Runtime>;
type MinVestedTransfer = MinVestedTransfer;
+ type VestedTransferOrigin = EnsureSigned<AccountId>;
type WeightInfo = ();
- const MAX_VESTING_SCHEDULES: u32 = 28;
+ type MaxVestingSchedules = MaxVestingSchedules;
+ type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;
}
parameter_types! {
@@ -721,7 +736,7 @@
impl pallet_common::Config for Runtime {
type Event = Event;
- type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;
+ type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;
@@ -762,20 +777,14 @@
pub const MaxScheduledPerBlock: u32 = 50;
}
-pub struct Sponsoring;
-impl SponsoringResolve<AccountId, Call> for Sponsoring {
- fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>
- where
- Call: Dispatchable<Info = DispatchInfo>,
- AccountId: AsRef<[u8]>,
- {
- pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)
- }
-}
-
+type EvmSponsorshipHandler = (
+ pallet_nft::NftEthSponsorshipHandler<Runtime>,
+ pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
+);
type SponsorshipHandler = (
pallet_nft::NftSponsorshipHandler<Runtime>,
//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
+ pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
);
impl pallet_unq_scheduler::Config for Runtime {
@@ -788,22 +797,17 @@
type MaxScheduledPerBlock = MaxScheduledPerBlock;
type SponsorshipHandler = SponsorshipHandler;
type WeightInfo = ();
-}
-
-impl pallet_nft_transaction_payment::Config for Runtime {
- type SponsorshipHandler = SponsorshipHandler;
}
impl pallet_evm_transaction_payment::Config for Runtime {
- type SponsorshipHandler = (
- pallet_nft::NftEthSponsorshipHandler<Self>,
- pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,
- );
+ type EvmSponsorshipHandler = EvmSponsorshipHandler;
type Currency = Balances;
+ type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
+ type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;
}
impl pallet_nft_charge_transaction::Config for Runtime {
- type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;
+ type SponsorshipHandler = SponsorshipHandler;
}
// impl pallet_contract_helpers::Config for Runtime {
@@ -841,7 +845,8 @@
Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,
Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,
System: system::{Pallet, Call, Storage, Config, Event<T>} = 36,
- Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,
+ Vesting: orml_vesting::{Pallet, Storage, Call, Event<T>, Config<T>} = 37,
+ // Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,
// Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,
// XCM helpers.
@@ -854,7 +859,7 @@
Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,
Nft: pallet_nft::{Pallet, Call, Storage} = 61,
Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,
- NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,
+ // free = 63
Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,
// ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,
Common: pallet_common::{Pallet, Storage, Event<T>} = 66,
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -19,9 +19,12 @@
addCollectionAdminExpectSuccess,
adminApproveFromExpectSuccess,
getCreatedCollectionCount,
+ transferFromExpectSuccess,
+ transferFromExpectFail,
} from './util/helpers';
chai.use(chaiAsPromised);
+const expect = chai.expect;
describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
let alice: IKeyringPair;
@@ -79,6 +82,320 @@
});
});
+describe('Normal user can approve other users to transfer:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ });
+
+ it('Fungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ });
+
+ it('ReFungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ });
+});
+
+describe('Approved users can transferFrom up to approved amount:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');
+ });
+
+ it('Fungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');
+ });
+
+ it('ReFungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');
+ });
+});
+
+describe('Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'NFT');
+ await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
+ });
+
+ it('Fungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'Fungible');
+ await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
+ });
+
+ it('ReFungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', bob.address);
+ await approveExpectSuccess(collectionId, itemId, bob, charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, charlie, bob, alice, 1, 'ReFungible');
+ await transferFromExpectFail(collectionId, itemId, charlie, bob, alice, 1);
+ });
+});
+
+describe('Approved amount decreases by the transferred amount.:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+ let dave: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ dave = privateKey('//Dave');
+ });
+ });
+
+ 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 () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);
+ await approveExpectSuccess(collectionId, itemId, alice, bob.address, 10);
+ await transferFromExpectSuccess(collectionId, itemId, bob, alice, charlie, 2, 'Fungible');
+ await transferFromExpectSuccess(collectionId, itemId, bob, alice, dave, 8, 'Fungible');
+ });
+});
+
+describe('User may clear the approvals to approving for 0 amount:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+ await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);
+ await approveExpectSuccess(collectionId, itemId, alice, bob.address, 0);
+ await transferFromExpectFail(collectionId, itemId, bob, bob, charlie, 1);
+ });
+
+ it('Fungible', async () => {
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 1);
+ await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 0);
+ await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, bob, charlie, 1);
+ });
+
+ it('ReFungible', async () => {
+ const reFungibleCollectionId =
+ await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 1);
+ await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);
+ await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, bob, charlie, 1);
+ });
+});
+
+describe('User cannot approve for the amount greater than they own:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('1 for NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
+ await approveExpectFail(collectionId, itemId, bob, charlie, 2);
+ });
+
+ it('Fungible', async () => {
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
+ await approveExpectFail(fungibleCollectionId, newFungibleTokenId, bob, charlie, 11);
+ });
+
+ it('ReFungible', async () => {
+ const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
+ await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, charlie, 101);
+ });
+});
+
+describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+ let dave: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ dave = privateKey('//Dave');
+ });
+ });
+
+ it('NFT', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');
+ await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+ await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'NFT');
+ });
+
+ it('Fungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');
+ await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+ await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'Fungible');
+ });
+
+ it('ReFungible up to an approved amount', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);
+ await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');
+ await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+ await transferFromExpectSuccess(collectionId, itemId, bob, dave, alice, 1, 'ReFungible');
+ });
+});
+
+describe('Repeated approvals add up', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+ let dave: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ dave = privateKey('//Dave');
+ });
+ });
+
+ it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ await createItemExpectSuccess(alice, collectionId, 'Fungible', alice.address);
+ await approveExpectSuccess(collectionId, 0, alice, bob.address, 1);
+ await approveExpectSuccess(collectionId, 0, alice, charlie.address, 1);
+ // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address);
+ // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address);
+ // expect(allowances1 + allowances2).to.be.eq(BigInt(2));
+ });
+
+ it.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);
+ await approveExpectSuccess(collectionId, itemId, alice, bob.address, 1);
+ await approveExpectSuccess(collectionId, itemId, alice, charlie.address, 1);
+ // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address);
+ // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address);
+ // expect(allowances1 + allowances2).to.be.eq(BigInt(2));
+ });
+
+ // Canceled by changing approve logic
+ 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 () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'Fungible', decimalPoints: 0 }});
+ await createItemExpectSuccess(alice, collectionId, 'Fungible', dave.address);
+ await approveExpectSuccess(collectionId, 0, dave, bob.address, 5);
+ await approveExpectFail(collectionId, 0, dave, charlie, 6);
+ });
+
+ // Canceled by changing approve logic
+ it.skip('Cannot approve for more than total users amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async () => {
+ const collectionId = await createCollectionExpectSuccess({ mode:{ type: 'ReFungible' } });
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', dave.address);
+ await approveExpectSuccess(collectionId, itemId, dave, bob.address, 50);
+ await approveExpectFail(collectionId, itemId, dave, charlie, 51);
+ });
+});
+
+describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+ let charlie: IKeyringPair;
+
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ charlie = privateKey('//Charlie');
+ });
+ });
+
+ it('can be called by collection admin on non-owned item', async () => {
+ const collectionId = await createCollectionExpectSuccess();
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+ await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
+ await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);
+ });
+});
+
describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
@@ -170,27 +487,5 @@
await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});
await approveExpectFail(collectionId, itemId, alice, charlie);
- });
-});
-
-describe('Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:', () => {
- let alice: IKeyringPair;
- let bob: IKeyringPair;
- let charlie: IKeyringPair;
-
- before(async () => {
- await usingApi(async () => {
- alice = privateKey('//Alice');
- bob = privateKey('//Bob');
- charlie = privateKey('//Charlie');
- });
- });
-
- it('can be called by collection admin on non-owned item', async () => {
- const collectionId = await createCollectionExpectSuccess();
- const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
-
- await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
- await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);
});
});
tests/src/confirmSponsorship.test.tsdiffbeforeafterboth--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -252,23 +252,20 @@
const zeroBalance = await findUnusedAddress(api);
// Mint token for alice
- const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', alice.address);
+ const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
- // Transfer this token from Alice to unused address and back
- // Alice to Zero gets sponsored
- const aliceToZero = api.tx.nft.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);
- const events1 = await submitTransactionAsync(alice, aliceToZero);
+ const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
+
+ // Zero to alice gets sponsored
+ const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
const result1 = getGenericResult(events1);
expect(result1.success).to.be.true;
// Second transfer should fail
const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
- const zeroToAlice = api.tx.nft.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
- const badTransaction = async function () {
- await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
- };
- await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');
+ await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');
const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
+ expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
// Try again after Zero gets some balance - now it should succeed
const balancetx = api.tx.balances.transfer(zeroBalance.address, 1e15);
@@ -276,8 +273,6 @@
const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
const result2 = getGenericResult(events2);
expect(result2.success).to.be.true;
-
- expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
});
});
tests/src/eth/marketplace/MarketPlace.abidiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlace.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_escrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_price","type":"uint256"},{"indexed":false,"internalType":"address","name":"_currencyCode","type":"address"},{"indexed":false,"internalType":"address","name":"_idCollection","type":"address"},{"indexed":false,"internalType":"uint256","name":"_idNFT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"AddedAsk","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_idCollection","type":"address"},{"indexed":false,"internalType":"uint256","name":"_idNFT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderPrice","type":"uint256"}],"name":"BoughtNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_idCollection","type":"address"},{"indexed":false,"internalType":"uint256","name":"_idNFT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderPrice","type":"uint256"}],"name":"BoughtNFT4KSM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_idCollection","type":"address"},{"indexed":false,"internalType":"uint256","name":"_idNFT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"CanceledAsk","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"_sender","type":"address"}],"name":"DepositedKSM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_price","type":"uint256"},{"indexed":false,"internalType":"address","name":"_currencyCode","type":"address"},{"indexed":false,"internalType":"address","name":"_idCollection","type":"address"},{"indexed":false,"internalType":"uint256","name":"_idNFT","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"_active","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"EditedAsk","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"_currencyCode","type":"address"},{"indexed":false,"internalType":"address","name":"_sender","type":"address"}],"name":"Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"}],"name":"WithdrawnAllKSM","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"addAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asksbySeller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceKSM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"buy","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"address","name":"_buyer","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"buyKSM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"cancelAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"depositKSM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"uint8","name":"_active","type":"uint8"}],"name":"editAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"getOrder","outputs":[{"components":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"address","name":"currencyCode","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"address","name":"idCollection","type":"address"},{"internalType":"address","name":"ownerAddr","type":"address"},{"internalType":"uint8","name":"flagActive","type":"uint8"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"}],"internalType":"struct MarketPlace.Order","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOrdersLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"orders","outputs":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"address","name":"currencyCode","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"address","name":"idCollection","type":"address"},{"internalType":"address","name":"ownerAddr","type":"address"},{"internalType":"uint8","name":"flagActive","type":"uint8"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"tokenURI","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newEscrow","type":"address"}],"name":"setEscrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_coin","type":"address"}],"name":"setNativeCoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newEscrow","type":"address"}],"name":"setowner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"address payable","name":"_sender","type":"address"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"}],"name":"withdrawAllKSM","outputs":[{"internalType":"uint256","name":"lastBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
\ No newline at end of file
tests/src/eth/marketplace/MarketPlace.bindiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlace.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b50604051620029b8380380620029b8833981016040819052620000349162000327565b600580546001600160a01b038084166001600160a01b03199283161790925560068054821633179055604080516101408101825260008082526020808301828152838501838152606085018481526080860185815260a0870186815260c0880187815289518088018b5288815260e08a019081528a518089018c528981526101008b01528a51808901909b52888b526101208a019a909a528754600181018955978052885160099098027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810198895595517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56487018054918e16918d1691909117905593517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56586015591517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566850155517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56784018054918b16919099161790975595517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56882018054975160ff16600160a01b026001600160a81b0319909816919098161795909517909555915180519194929362000239937f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5699091019291019062000281565b5061010082015180516200025891600784019160209091019062000281565b5061012082015180516200027791600884019160209091019062000281565b5050505062000396565b8280546200028f9062000359565b90600052602060002090601f016020900481019282620002b35760008555620002fe565b82601f10620002ce57805160ff1916838001178555620002fe565b82800160010185558215620002fe579182015b82811115620002fe578251825591602001919060010190620002e1565b506200030c92915062000310565b5090565b5b808211156200030c576000815560010162000311565b6000602082840312156200033a57600080fd5b81516001600160a01b03811681146200035257600080fd5b9392505050565b600181811c908216806200036e57607f821691505b602082108114156200039057634e487b7160e01b600052602260045260246000fd5b50919050565b61261280620003a66000396000f3fe6080604052600436106101025760003560e01c806399f2d4eb11610095578063c10c354611610064578063c10c354614610221578063cce7ec1314610378578063e22d4f7d1461038b578063edb25841146103c3578063fd8868c7146103f057610184565b806399f2d4eb146102c5578063a85c38ef146102e5578063b460af941461031b578063b4c3e8ea1461034b57610184565b80636b084b25116100d15780636b084b2514610241578063791bd866146102615780638c088f001461028157806393cdd334146102a557610184565b806337c4eb58146101bf5780633ce61756146101e157806340b8074614610201578063592bd7051461022157610184565b366101845760405162461bcd60e51b815260206004820152604160248201527f43616e277420616363657074207061796d656e7420776974686f757420636f6c60448201527f6c656374696f6e20616e64204944732c20757365206441707020746f2073656e6064820152601960fa1b608482015260a4015b60405180910390fd5b60405162461bcd60e51b815260206004820152601060248201526f27379039bab1b410333ab731ba34b7b760811b604482015260640161017b565b3480156101cb57600080fd5b506101df6101da366004612054565b610410565b005b3480156101ed57600080fd5b506101df6101fc36600461209c565b610979565b34801561020d57600080fd5b506101df61021c3660046120c0565b6109c5565b34801561022d57600080fd5b506101df61023c36600461209c565b610c27565b34801561024d57600080fd5b506101df61025c3660046120ec565b610c73565b34801561026d57600080fd5b506101df61027c366004612151565b610ee6565b34801561028d57600080fd5b506000545b6040519081526020015b60405180910390f35b3480156102b157600080fd5b506102926102c036600461209c565b610fb1565b3480156102d157600080fd5b506102926102e03660046120c0565b61105e565b3480156102f157600080fd5b50610305610300366004612181565b61108f565b60405161029c9a999897969594939291906121f6565b34801561032757600080fd5b5061033b610336366004612283565b6112a0565b604051901515815260200161029c565b34801561035757600080fd5b5061029261036636600461209c565b60026020526000908152604090205481565b61033b6103863660046120c0565b6113d2565b34801561039757600080fd5b506102926103a63660046120c0565b600360209081526000928352604080842090915290825290205481565b3480156103cf57600080fd5b506103e36103de3660046120c0565b61181b565b60405161029c91906122c5565b3480156103fc57600080fd5b506101df61040b3660046123a5565b611b0c565b6040516331a9108f60e11b8152600481018290526000906001600160a01b03841690636352211e90602401602060405180830381865afa158015610458573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061047c91906123f8565b90506001600160a01b03811633146104d65760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920746f6b656e206f776e65722063616e206d616b652061736b000000604482015260640161017b565b6060806060856001600160a01b03166306fdde036040518163ffffffff1660e01b8152600401600060405180830381865afa92505050801561053a57506040513d6000823e601f3d908101601f19168201604052610537919081019061242b565b60015b61055557604051806020016040528060008152509250610558565b92505b856001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa9250505080156105b757506040513d6000823e601f3d908101601f191682016040526105b4919081019061242b565b60015b6105d2576040518060200160405280600081525091506105d5565b91505b60405163c87b56dd60e01b8152600481018690526001600160a01b0387169063c87b56dd90602401600060405180830381865afa92505050801561063b57506040513d6000823e601f3d908101601f19168201604052610638919081019061242b565b60015b6106545750604080516020810190915260008152610657565b90505b60408051610140810182528681526001600160a01b03808a1660208084019182529383018c815242606085019081528b8416608086019081523360a08701908152600160c0880181815260e089018d81526101008a018d90526101208a018c90526000805493840181558052895160099093027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563810193845597517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56489018054918b166001600160a01b031992831617905596517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56589015594517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56688015592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567870180549189169190961617909455517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56885018054925160ff16600160a01b026001600160a81b03199093169190961617179093559151805193949293610828937f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e569909301929190910190611fa3565b506101008201518051610845916007840191602090910190611fa3565b506101208201518051610862916008840191602090910190611fa3565b505060008054909150610877906001906124ee565b6001600160a01b03881660008181526003602090815260408083208b84528252808320859055338084526004808452828520805460018101825590865293909420909201859055516323b872dd60e01b815293945091926323b872dd926108e492909130918c9101612505565b600060405180830381600087803b1580156108fe57600080fd5b505af1158015610912573d6000803e3d6000fd5b5050604080518c81526001600160a01b038c811660208301528b1681830152606081018a90526080810185905290517f83b3a8754d705365b581ab63c329c1e55255a22aa78c67b5fcef378bfb2d9b9993509081900360a0019150a1505050505050505050565b6006546001600160a01b031633146109a35760405162461bcd60e51b815260040161017b90612529565b600780546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0382166000908152600360209081526040808320848452909152812054815490913391839081106109ff576109ff612551565b60009182526020909120600560099092020101546001600160a01b031614610a695760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920746f6b656e206f776e65722063616e20656469742061736b000000604482015260640161017b565b60008181548110610a7c57610a7c612551565b6000918252602090912060099091020160050154600160a01b900460ff16610adb5760405162461bcd60e51b8152602060048201526012602482015271151a1a5cc8185cdac81a5cc818db1bdcd95960721b604482015260640161017b565b4260008281548110610aef57610aef612551565b9060005260206000209060090201600301819055506000808281548110610b1857610b18612551565b906000526020600020906009020160050160146101000a81548160ff021916908360ff160217905550826001600160a01b03166323b872dd3060008481548110610b6457610b64612551565b60009182526020909120600560099092020101546040516001600160e01b031960e085901b168152610ba592916001600160a01b0316908790600401612505565b600060405180830381600087803b158015610bbf57600080fd5b505af1158015610bd3573d6000803e3d6000fd5b5050604080516001600160a01b0387168152602081018690529081018490527f6330f58b476cc7f9dd8db8130a0fbb49b291ebc32531eb36e9646f56ee20174b9250606001905060405180910390a1505050565b6006546001600160a01b03163314610c515760405162461bcd60e51b815260040161017b90612529565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038316600090815260036020908152604080832085845290915281205481549091339183908110610cad57610cad612551565b60009182526020909120600560099092020101546001600160a01b031614610d175760405162461bcd60e51b815260206004820152601d60248201527f4f6e6c7920746f6b656e206f776e65722063616e20656469742061736b000000604482015260640161017b565b60008181548110610d2a57610d2a612551565b6000918252602090912060099091020160050154600160a01b900460ff16610d895760405162461bcd60e51b8152602060048201526012602482015271151a1a5cc8185cdac81a5cc818db1bdcd95960721b604482015260640161017b565b8515610db9578560008281548110610da357610da3612551565b9060005260206000209060090201600201819055505b6001600160a01b03851615610e12578460008281548110610ddc57610ddc612551565b906000526020600020906009020160010160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b4260008281548110610e2657610e26612551565b9060005260206000209060090201600301819055508160008281548110610e4f57610e4f612551565b600091825260209182902060099190910201600501805460ff60a01b1916600160a01b60ff94851602179055604080518981526001600160a01b03898116938201939093529187169082015260608101859052908316608082015260a081018290527f04211d838289b72af597bf28a4ccff5a30d6056f5fce2eddb127ab4ffea0963d9060c00160405180910390a1505050505050565b6005546001600160a01b03163314610f325760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b604482015260640161017b565b6001600160a01b038116600090815260026020526040902054610f56908390612567565b6001600160a01b038216600081815260026020908152604091829020939093558051858152928301919091527f291ce0b690061cb1b3f1111bf6500efe526983251fb0f863251ec3670d035fc4910160405180910390a15050565b6005546000906001600160a01b031633146110005760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b604482015260640161017b565b506001600160a01b03811660008181526002602090815260408083208054939055805193845290830182905290917f027aeacbc6fbfa7fe735ff8f14a600469ad1a3197ce1c8c8a159641cf68fe592910160405180910390a1919050565b6004602052816000526040600020818154811061107a57600080fd5b90600052602060002001600091509150505481565b6000818154811061109f57600080fd5b600091825260209091206009909102018054600182015460028301546003840154600485015460058601546006870180549698506001600160a01b0395861697949693959283169492821693600160a01b90920460ff1692916111019061257f565b80601f016020809104026020016040519081016040528092919081815260200182805461112d9061257f565b801561117a5780601f1061114f5761010080835404028352916020019161117a565b820191906000526020600020905b81548152906001019060200180831161115d57829003601f168201915b50505050509080600701805461118f9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546111bb9061257f565b80156112085780601f106111dd57610100808354040283529160200191611208565b820191906000526020600020905b8154815290600101906020018083116111eb57829003601f168201915b50505050509080600801805461121d9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546112499061257f565b80156112965780601f1061126b57610100808354040283529160200191611296565b820191906000526020600020905b81548152906001019060200180831161127957829003601f168201915b505050505090508a565b6006546000906001600160a01b031633146112cd5760405162461bcd60e51b815260040161017b90612529565b6007546001600160a01b0384811691161461135b5760405163a9059cbb60e01b81526001600160a01b0383811660048301526024820186905284169063a9059cbb906044016020604051808303816000875af1158015611331573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061135591906125ba565b50611382565b6040516001600160a01b0383169085156108fc029086906000818181858888f19450505050505b604080518581526001600160a01b03858116602083015284168183015290517fb524bace85e4ce0f8da306e32f39263c5724780ca00b7551c051c8e3dc36adaf9181900360600190a19392505050565b6001600160a01b0382166000908152600360209081526040808320848452909152812054815482918291811061140a5761140a612551565b600091825260209182902060408051610140810182526009939093029091018054835260018101546001600160a01b039081169484019490945260028101549183019190915260038101546060830152600481015483166080830152600581015492831660a0830152600160a01b90920460ff1660c082015260068201805491929160e08401919061149b9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546114c79061257f565b80156115145780601f106114e957610100808354040283529160200191611514565b820191906000526020600020905b8154815290600101906020018083116114f757829003601f168201915b5050505050815260200160078201805461152d9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546115599061257f565b80156115a65780601f1061157b576101008083540402835291602001916115a6565b820191906000526020600020905b81548152906001019060200180831161158957829003601f168201915b505050505081526020016008820180546115bf9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546115eb9061257f565b80156116385780601f1061160d57610100808354040283529160200191611638565b820191906000526020600020905b81548152906001019060200180831161161b57829003601f168201915b5050505050815250509050806040015134146116ac5760405162461bcd60e51b815260206004820152602d60248201527f4e6f7420726967687420616d6f756e742073656e742c206861766520746f206260448201526c6520657175616c20707269636560981b606482015260840161017b565b6001600160a01b0384166000908152600360209081526040808320868452909152812054815482919081106116e3576116e3612551565b906000526020600020906009020160050160146101000a81548160ff021916908360ff160217905550836001600160a01b03166323b872dd3033866040518463ffffffff1660e01b815260040161173c93929190612505565b600060405180830381600087803b15801561175657600080fd5b505af115801561176a573d6000803e3d6000fd5b505050508060a001516001600160a01b03166108fc82604001519081150290604051600060405180830381858888f16001600160a01b03891660008181526003602090815260408083208c8452825291829020548983015183519485529184018c90529183019190915260608201529096507fc220cf2a12d6492e23ed1141c55018997799699d009b00ebd5a4874c60f5c31e9450608001925061180c915050565b60405180910390a15092915050565b61188f6040518061014001604052806000815260200160006001600160a01b03168152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600060ff1681526020016060815260200160608152602001606081525090565b6001600160a01b038316600090815260036020908152604080832085845290915281205481549091908190839081106118ca576118ca612551565b600091825260209182902060408051610140810182526009939093029091018054835260018101546001600160a01b039081169484019490945260028101549183019190915260038101546060830152600481015483166080830152600581015492831660a0830152600160a01b90920460ff1660c082015260068201805491929160e08401919061195b9061257f565b80601f01602080910402602001604051908101604052809291908181526020018280546119879061257f565b80156119d45780601f106119a9576101008083540402835291602001916119d4565b820191906000526020600020905b8154815290600101906020018083116119b757829003601f168201915b505050505081526020016007820180546119ed9061257f565b80601f0160208091040260200160405190810160405280929190818152602001828054611a199061257f565b8015611a665780601f10611a3b57610100808354040283529160200191611a66565b820191906000526020600020905b815481529060010190602001808311611a4957829003601f168201915b50505050508152602001600882018054611a7f9061257f565b80601f0160208091040260200160405190810160405280929190818152602001828054611aab9061257f565b8015611af85780601f10611acd57610100808354040283529160200191611af8565b820191906000526020600020905b815481529060010190602001808311611adb57829003601f168201915b505050919092525091979650505050505050565b6001600160a01b038416600090815260036020908152604080832086845290915281205481548291908110611b4357611b43612551565b600091825260209182902060408051610140810182526009939093029091018054835260018101546001600160a01b039081169484019490945260028101549183019190915260038101546060830152600481015483166080830152600581015492831660a0830152600160a01b90920460ff1660c082015260068201805491929160e084019190611bd49061257f565b80601f0160208091040260200160405190810160405280929190818152602001828054611c009061257f565b8015611c4d5780601f10611c2257610100808354040283529160200191611c4d565b820191906000526020600020905b815481529060010190602001808311611c3057829003601f168201915b50505050508152602001600782018054611c669061257f565b80601f0160208091040260200160405190810160405280929190818152602001828054611c929061257f565b8015611cdf5780601f10611cb457610100808354040283529160200191611cdf565b820191906000526020600020905b815481529060010190602001808311611cc257829003601f168201915b50505050508152602001600882018054611cf89061257f565b80601f0160208091040260200160405190810160405280929190818152602001828054611d249061257f565b8015611d715780601f10611d4657610100808354040283529160200191611d71565b820191906000526020600020905b815481529060010190602001808311611d5457829003601f168201915b505050919092525050600554919250506001600160a01b0316331480611d9f5750336001600160a01b038416145b611df75760405162461bcd60e51b8152602060048201526024808201527f4f6e6c7920657363726f77206f722062757965722063616e2063616c6c206275604482015263794b534d60e01b606482015260840161017b565b6040808201516001600160a01b038516600090815260026020529190912054611e2091906124ee565b6001600160a01b03808516600090815260026020526040808220939093558383015160a085015190921681529190912054611e5b9190612567565b60a08201516001600160a01b039081166000908152600260209081526040808320949094559188168152600382528281208782529091529081205481548291908110611ea957611ea9612551565b906000526020600020906009020160050160146101000a81548160ff021916908360ff160217905550846001600160a01b03166323b872dd3084876040518463ffffffff1660e01b8152600401611f0293929190612505565b600060405180830381600087803b158015611f1c57600080fd5b505af1158015611f30573d6000803e3d6000fd5b505050506001600160a01b038516600081815260036020908152604080832088845282529182902054848301518351948552918401889052838301526060830152517f68bdd3548f3f05742b376651745ca73472eb18f2230f64b893d7a48e575eb0149181900360800190a15050505050565b828054611faf9061257f565b90600052602060002090601f016020900481019282611fd15760008555612017565b82601f10611fea57805160ff1916838001178555612017565b82800160010185558215612017579182015b82811115612017578251825591602001919060010190611ffc565b50612023929150612027565b5090565b5b808211156120235760008155600101612028565b6001600160a01b038116811461205157600080fd5b50565b6000806000806080858703121561206a57600080fd5b84359350602085013561207c8161203c565b9250604085013561208c8161203c565b9396929550929360600135925050565b6000602082840312156120ae57600080fd5b81356120b98161203c565b9392505050565b600080604083850312156120d357600080fd5b82356120de8161203c565b946020939093013593505050565b600080600080600060a0868803121561210457600080fd5b8535945060208601356121168161203c565b935060408601356121268161203c565b925060608601359150608086013560ff8116811461214357600080fd5b809150509295509295909350565b6000806040838503121561216457600080fd5b8235915060208301356121768161203c565b809150509250929050565b60006020828403121561219357600080fd5b5035919050565b60005b838110156121b557818101518382015260200161219d565b838111156121c4576000848401525b50505050565b600081518084526121e281602086016020860161219a565b601f01601f19169290920160200192915050565b8a81526001600160a01b038a81166020830152604082018a9052606082018990528781166080830152861660a082015260ff851660c082015261014060e08201819052600090612248838201876121ca565b905082810361010084015261225d81866121ca565b905082810361012084015261227281856121ca565b9d9c50505050505050505050505050565b60008060006060848603121561229857600080fd5b8335925060208401356122aa8161203c565b915060408401356122ba8161203c565b809150509250925092565b6020815281516020820152600060208301516122ec60408401826001600160a01b03169052565b506040830151606083015260608301516080830152608083015161231b60a08401826001600160a01b03169052565b5060a08301516001600160a01b03811660c08401525060c083015160ff811660e08401525060e0830151610140610100818186015261235e6101608601846121ca565b9250808601519050601f1961012081878603018188015261237f85846121ca565b90880151878203909201848801529350905061239b83826121ca565b9695505050505050565b600080600080608085870312156123bb57600080fd5b84356123c68161203c565b93506020850135925060408501356123dd8161203c565b915060608501356123ed8161203c565b939692955090935050565b60006020828403121561240a57600080fd5b81516120b98161203c565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561243d57600080fd5b815167ffffffffffffffff8082111561245557600080fd5b818401915084601f83011261246957600080fd5b81518181111561247b5761247b612415565b604051601f8201601f19908116603f011681019083821181831017156124a3576124a3612415565b816040528281528760208487010111156124bc57600080fd5b6124cd83602083016020880161219a565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b600082821015612500576125006124d8565b500390565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6020808252600e908201526d27b7363c9037bbb732b91031b0b760911b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000821982111561257a5761257a6124d8565b500190565b600181811c9082168061259357607f821691505b602082108114156125b457634e487b7160e01b600052602260045260246000fd5b50919050565b6000602082840312156125cc57600080fd5b815180151581146120b957600080fdfea26469706673582212205a8295bbb792776496ccaaa9cb7384efe95af6490ca8015ceb6eb22e5ab3ae6b64736f6c634300080a0033
\ No newline at end of file
tests/src/eth/marketplace/MarketPlace.soldiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/marketplace/MarketPlace.sol
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: Apache License
+pragma solidity >=0.8.0;
+import "../api/UniqueNFT.sol";
+
+// Inline
+interface ERC20Events {
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event Approval(
+ address indexed owner,
+ address indexed spender,
+ uint256 value
+ );
+}
+
+interface ERC20 is Dummy, ERC165, ERC20Events {
+ // Selector: name() 06fdde03
+ function name() external view returns (string memory);
+
+ // Selector: symbol() 95d89b41
+ function symbol() external view returns (string memory);
+
+ // Selector: totalSupply() 18160ddd
+ function totalSupply() external view returns (uint256);
+
+ // Selector: decimals() 313ce567
+ function decimals() external view returns (uint8);
+
+ // Selector: balanceOf(address) 70a08231
+ function balanceOf(address owner) external view returns (uint256);
+
+ // Selector: transfer(address,uint256) a9059cbb
+ function transfer(address to, uint256 amount) external returns (bool);
+
+ // Selector: transferFrom(address,address,uint256) 23b872dd
+ function transferFrom(
+ address from,
+ address to,
+ uint256 amount
+ ) external returns (bool);
+
+ // Selector: approve(address,uint256) 095ea7b3
+ function approve(address spender, uint256 amount) external returns (bool);
+
+ // Selector: allowance(address,address) dd62ed3e
+ function allowance(address owner, address spender)
+ external
+ view
+ returns (uint256);
+}
+
+interface UniqueFungible is Dummy, ERC165, ERC20 {}
+
+contract MarketPlace {
+ struct Order {
+ uint256 idNFT;
+ address currencyCode; // UNIQ tokens as address address (1); wKSM
+ uint256 price;
+ uint256 time;
+ address idCollection;
+ address ownerAddr;
+ uint8 flagActive;
+ string name;
+ string symbol;
+ string tokenURI;
+ }
+ Order[] public orders;
+ uint256 test;
+ mapping(address => uint256) public balanceKSM; // [ownerAddr][currency] => [KSMs]
+ mapping(address => mapping(uint256 => uint256)) public asks; // [buyer][idCollection][idNFT] => idorder
+
+ mapping(address => uint256[]) public asksbySeller; // [addressSeller] =>idorder
+
+ address escrow;
+ address owner;
+ address nativecoin;
+
+ constructor(address _escrow) {
+ escrow = _escrow;
+ owner = msg.sender;
+
+ orders.push(
+ Order(0, address(0), 0, 0, address(0), address(0), 0, "", "", "")
+ );
+ }
+
+ function setowner(address _newEscrow) public onlyOwner {
+ escrow = _newEscrow;
+ }
+
+ function setEscrow(address _newEscrow) public onlyOwner {
+ escrow = _newEscrow;
+ }
+
+ function setNativeCoin(address _coin) public onlyOwner {
+ nativecoin = _coin;
+ }
+
+ modifier onlyEscrow() {
+ require(msg.sender == escrow, "Only escrow can");
+ _;
+ }
+
+ modifier onlyOwner() {
+ require(msg.sender == owner, "Only owner can");
+ _;
+ }
+
+ /**
+ * Make bids (orders) to sell NFTs
+ */
+
+ receive() external payable {
+ revert(
+ "Can't accept payment without collection and IDs, use dApp to send"
+ );
+ }
+
+ fallback() external payable {
+ revert("No such function");
+ }
+
+ event AddedAsk(
+ uint256 _price,
+ address _currencyCode,
+ address _idCollection,
+ uint256 _idNFT,
+ uint256 orderId
+ );
+ event EditedAsk(
+ uint256 _price,
+ address _currencyCode,
+ address _idCollection,
+ uint256 _idNFT,
+ uint8 _active,
+ uint256 orderId
+ );
+
+ event CanceledAsk(address _idCollection, uint256 _idNFT, uint256 orderId);
+
+ event DepositedKSM(uint256 _amount, address _sender);
+
+ event BoughtNFT4KSM(
+ address _idCollection,
+ uint256 _idNFT,
+ uint256 orderID,
+ uint256 orderPrice
+ );
+
+ event BoughtNFT(
+ address _idCollection,
+ uint256 _idNFT,
+ uint256 orderID,
+ uint256 orderPrice
+ );
+
+ event WithdrawnAllKSM(address _sender, uint256 balance);
+
+ event Withdrawn(uint256 _amount, address _currencyCode, address _sender);
+
+ function addAsk(
+ uint256 _price,
+ address _currencyCode,
+ address _idCollection,
+ uint256 _idNFT
+ ) public {
+ //
+ address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT);
+ require(ownerNFT == msg.sender, "Only token owner can make ask");
+ string memory nameNFT;
+ string memory symbolNFT;
+ string memory uriNFT;
+ try UniqueNFT(_idCollection).name() returns (string memory name_) {
+ nameNFT = name_;
+ } catch {
+ nameNFT = "";
+ }
+ try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) {
+ symbolNFT = symbol_;
+ } catch {
+ symbolNFT = "";
+ }
+ try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (
+ string memory uri_
+ ) {
+ uriNFT = uri_;
+ } catch {
+ uriNFT = "";
+ }
+ orders.push(
+ Order(
+ _idNFT,
+ _currencyCode,
+ _price,
+ block.timestamp,
+ _idCollection,
+ msg.sender,
+ 1, // 1 = is active
+ nameNFT,
+ symbolNFT,
+ uriNFT
+ )
+ );
+
+ uint256 orderId = orders.length - 1;
+ asks[_idCollection][_idNFT] = orderId;
+ asksbySeller[msg.sender].push(orderId);
+ UniqueNFT(_idCollection).transferFrom(
+ msg.sender,
+ address(this),
+ _idNFT
+ );
+ emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId);
+ }
+
+ function editAsk(
+ uint256 _price,
+ address _currencyCode,
+ address _idCollection,
+ uint256 _idNFT,
+ uint8 _active
+ ) public {
+ uint256 orderID = asks[_idCollection][_idNFT];
+
+ require(
+ orders[orderID].ownerAddr == msg.sender,
+ "Only token owner can edit ask"
+ );
+ require(orders[orderID].flagActive != 0, "This ask is closed");
+ if (_price > 0) {
+ orders[orderID].price = _price;
+ }
+
+ if (_currencyCode != address(0)) {
+ orders[orderID].currencyCode = _currencyCode;
+ }
+
+ orders[orderID].time = block.timestamp;
+ orders[orderID].flagActive = _active;
+
+ emit EditedAsk(
+ _price,
+ _currencyCode,
+ _idCollection,
+ _idNFT,
+ _active,
+ orderID
+ );
+ }
+
+ function cancelAsk(address _idCollection, uint256 _idNFT) public {
+ uint256 orderID = asks[_idCollection][_idNFT];
+
+ require(
+ orders[orderID].ownerAddr == msg.sender,
+ "Only token owner can edit ask"
+ );
+ require(orders[orderID].flagActive != 0, "This ask is closed");
+
+ orders[orderID].time = block.timestamp;
+ orders[orderID].flagActive = 0;
+ UniqueNFT(_idCollection).transferFrom(
+ address(this),
+ orders[orderID].ownerAddr,
+ _idNFT
+ );
+ emit CanceledAsk(_idCollection, _idNFT, orderID);
+ }
+
+ function depositKSM(uint256 _amount, address _sender) public onlyEscrow {
+ balanceKSM[_sender] = balanceKSM[_sender] + _amount;
+ emit DepositedKSM(_amount, _sender);
+ }
+
+ function buyKSM(
+ address _idCollection,
+ uint256 _idNFT,
+ address _buyer,
+ address _receiver
+ ) public {
+ Order memory order = orders[asks[_idCollection][_idNFT]];
+ require(msg.sender == escrow || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" );
+ //1. reduce balance
+ balanceKSM[_buyer] = balanceKSM[_buyer] - order.price;
+ balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price;
+ // 2. close order
+ orders[asks[_idCollection][_idNFT]].flagActive = 0;
+ // 3. transfer NFT to buyer
+ UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT);
+ emit BoughtNFT4KSM(
+ _idCollection,
+ _idNFT,
+ asks[_idCollection][_idNFT],
+ order.price
+ );
+ }
+
+ function buy(address _idCollection, uint256 _idNFT)
+ public
+ payable
+ returns (bool result)
+ {
+ //buing for UNQ like as ethers
+
+ Order memory order = orders[asks[_idCollection][_idNFT]];
+ //1. check sent amount and send to seller
+ require(
+ msg.value == order.price,
+ "Not right amount sent, have to be equal price"
+ );
+ // 2. close order
+ orders[asks[_idCollection][_idNFT]].flagActive = 0;
+
+ // 3. transfer NFT to buyer
+ UniqueNFT(_idCollection).transferFrom(
+ address(this),
+ msg.sender,
+ _idNFT
+ );
+ //uint balance = address(this).balance;
+ result = payable(order.ownerAddr).send(order.price);
+ emit BoughtNFT(
+ _idCollection,
+ _idNFT,
+ asks[_idCollection][_idNFT],
+ order.price
+ );
+ }
+
+ /*
+ function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public { //buy for sny token if seller wants
+
+ Order memory order = orders[ asks[_idCollection][_idNFT]];
+ //1. check sent amount and transfer from buyer to seller
+ require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" );
+ // !!! transfer have to be approved to marketplace!
+ IERC20(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address
+ IERC20(order.currencyCode).transfer(order.ownerAddr, order.price);
+ // 2. close order
+ orders[ asks[_idCollection][_idNFT]].flagActive = 0;
+ // 3. transfer NFT to buyer
+ IERC721ext(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+
+
+ }
+ */
+
+ function withdrawAllKSM(address _sender)
+ public
+ onlyEscrow
+ returns (uint256 lastBalance)
+ {
+ lastBalance = balanceKSM[_sender];
+ balanceKSM[_sender] = 0;
+ emit WithdrawnAllKSM(_sender, lastBalance);
+ }
+
+ function withdraw(
+ uint256 _amount,
+ address _currencyCode,
+ address payable _sender
+ ) public onlyOwner returns (bool result) {
+ if (_currencyCode != nativecoin) {
+ //erc20 compat. tokens on UNIQUE chain
+ // uint balance = IERC20(_currencyCode).balanceOf(address(this));
+ UniqueFungible(_currencyCode).transfer(_sender, _amount);
+ } else {
+ // uint balance = address(this).balance;
+
+ result = (_sender).send(_amount); // for UNQ like as ethers
+ }
+ emit Withdrawn(_amount, _currencyCode, _sender);
+ return result;
+ }
+
+ // event GettedOrder(uint , Order);
+ function getOrder(address _idCollection, uint256 _idNFT)
+ public
+ view
+ returns (Order memory)
+ {
+ uint256 orderId = asks[_idCollection][_idNFT];
+ Order memory order = orders[orderId];
+ // emit GettedOrder (orderId, order);
+ return order;
+ }
+
+ function getOrdersLen() public view returns (uint256) {
+ return orders.length;
+ }
+}
tests/src/eth/marketplace/MarketPlaceKSM.abidiffbeforeafterbothno changes
tests/src/eth/marketplace/MarketPlaceKSM.bindiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceKSM.bin
+++ /dev/null
@@ -1 +0,0 @@
-608060405234801561001057600080fd5b50604051610e27380380610e2783398101604081905261002f9161005d565b600480546001600160a01b039092166001600160a01b0319928316179055600580549091163317905561008d565b60006020828403121561006f57600080fd5b81516001600160a01b038116811461008657600080fd5b9392505050565b610d8b8061009c6000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c806399f2d4eb1161007157806399f2d4eb1461018e5780639df55f52146101a1578063c10c35461461010f578063cce7ec13146101b4578063db87db52146101c7578063e22d4f7d146101fa57600080fd5b80630ad58d2f146100ae57806310a5ff17146100d6578063592bd7051461010f5780638a72ea6a146101245780638dbdbe6d1461017b575b600080fd5b6100c16100bc366004610c4d565b610225565b60405190151581526020015b60405180910390f35b6101016100e4366004610bd8565b600160209081526000928352604080842090915290825290205481565b6040519081526020016100cd565b61012261011d366004610b97565b6102d8565b005b610137610132366004610c04565b610345565b6040805197885260208801969096529486019390935260608501919091526001600160a01b0390811660808501521660a083015260ff1660c082015260e0016100cd565b610122610189366004610c4d565b6103a4565b61010161019c366004610bd8565b610449565b6101226101af366004610c86565b61047a565b6101226101c2366004610bd8565b610984565b6101016101d5366004610c1d565b6001600160a01b03166000908152600160209081526040808320938352929052205490565b610101610208366004610bd8565b600260209081526000928352604080842090915290825290205481565b6004546000906001600160a01b031633146102795760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b60448201526064015b60405180910390fd5b6001600160a01b03821660009081526001602090815260408083208684529091529020546102a8908590610cfa565b6001600160a01b039290921660009081526001602081815260408084209684529590529390209190915550919050565b6005546001600160a01b031633146103235760405162461bcd60e51b815260206004820152600e60248201526d27b7363c9037bbb732b91031b0b760911b6044820152606401610270565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000818154811061035557600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b0391821691811690600160a01b900460ff1687565b6004546001600160a01b031633146103f05760405162461bcd60e51b815260206004820152600f60248201526e27b7363c9032b9b1b937bb9031b0b760891b6044820152606401610270565b6001600160a01b038116600090815260016020908152604080832085845290915290205461041f908490610ce2565b6001600160a01b039091166000908152600160209081526040808320948352939052919091205550565b6003602052816000526040600020818154811061046557600080fd5b90600052602060002001600091509150505481565b6040516331a9108f60e11b81526004810183905233906001600160a01b03851690636352211e9060240160206040518083038186803b1580156104bc57600080fd5b505afa1580156104d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f49190610bbb565b6001600160a01b0316146105425760405162461bcd60e51b81526020600482015260156024820152742737ba103934b3b43a103a37b5b2b71037bbb732b960591b6044820152606401610270565b6001600160a01b0383166000908152600260209081526040808320858452909152812054905415806105ac575060006001600160a01b03166000828154811061058d5761058d610d27565b60009182526020909120600460069092020101546001600160a01b0316145b15610793576040805160e0810182528481526020810187815291810188815242606083019081526001600160a01b03808916608085019081523360a0860190815260ff808a1660c088019081526000805460018181018355828052995160069091027f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56381019190915599517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5648b015596517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5658a015594517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56689015591517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567880180549185166001600160a01b0319909216919091179055517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56890960180549351909116600160a01b026001600160a81b031990931695909116949094171790925590546107399190610cfa565b6001600160a01b038516600090815260026020908152604080832087845282528083209390935533825260039052908120905461077890600190610cfa565b81546001810183556000928352602090922090910155610916565b6040805160e0810182526001600160a01b03861660009081526002602090815283822087835290529182205482549192839290919081106107d6576107d6610d27565b60009182526020808320600690920290910154835282810189905260408084018b90524260608501526001600160a01b0389168352600282528083208884529091528120548154608090930192811061083157610831610d27565b60009182526020808320600460069093020191909101546001600160a01b039081168452338483015260ff871660409485015288168252600281528282208783529052908120548154811061088857610888610d27565b60009182526020918290208351600690920201908155908201516001820155604082015160028201556060820151600382015560808201516004820180546001600160a01b039283166001600160a01b031990911617905560a08301516005909201805460c09094015160ff16600160a01b026001600160a81b031990941692909116919091179190911790555b6040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b038516906323b872dd90606401600060405180830381600087803b15801561096457600080fd5b505af1158015610978573d6000803e3d6000fd5b50505050505050505050565b6001600160a01b0382166000908152600260209081526040808320848452909152812054815482919081106109bb576109bb610d27565b600091825260208083206040805160e0810182526006949094029091018054845260018082015485850190815260028301548685018190526003840154606088015260048401546001600160a01b03908116608089015260059094015493841660a0880152600160a01b90930460ff1660c08701523387529084528286209051865290925290922054909250610a519190610cfa565b33600090815260016020818152604080842086830180518652908352818520959095558581015160a08701516001600160a01b031685529282528084209451845293905291902054610aa39190610ce2565b60a08201516001600160a01b0390811660009081526001602090815260408083208287015184528252808320949094559186168152600282528281208582529091529081205481548291908110610afc57610afc610d27565b60009182526020909120600560069092020101805460ff92909216600160a01b0260ff60a01b199092169190911790556040516323b872dd60e01b8152306004820152336024820152604481018390526001600160a01b038416906323b872dd90606401600060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b50505050505050565b600060208284031215610ba957600080fd5b8135610bb481610d3d565b9392505050565b600060208284031215610bcd57600080fd5b8151610bb481610d3d565b60008060408385031215610beb57600080fd5b8235610bf681610d3d565b946020939093013593505050565b600060208284031215610c1657600080fd5b5035919050565b60008060408385031215610c3057600080fd5b823591506020830135610c4281610d3d565b809150509250929050565b600080600060608486031215610c6257600080fd5b83359250602084013591506040840135610c7b81610d3d565b809150509250925092565b600080600080600060a08688031215610c9e57600080fd5b85359450602086013593506040860135610cb781610d3d565b925060608601359150608086013560ff81168114610cd457600080fd5b809150509295509295909350565b60008219821115610cf557610cf5610d11565b500190565b600082821015610d0c57610d0c610d11565b500390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0381168114610d5257600080fd5b5056fea2646970667358221220d098ae4e0cc7bdbb7de1061c03402a667836f903122c1c606ca2c65db1ab78e964736f6c63430008070033
\ No newline at end of file
tests/src/eth/marketplace/MarketPlaceKSM.soldiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceKSM.sol
+++ /dev/null
@@ -1,179 +0,0 @@
-// SPDX-License-Identifier: Apache License
-pragma solidity >=0.8.0;
-
-interface IERC721 {
- event Transfer(
- address indexed from,
- address indexed to,
- uint256 indexed tokenId
- );
- event Approval(
- address indexed owner,
- address indexed approved,
- uint256 indexed tokenId
- );
-
- function ownerOf(uint256 tokenId) external view returns (address owner);
-
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external;
-
- function debug(string memory value) external;
-}
-
-interface IERC20 {
- event Transfer(address indexed from, address indexed to, uint256 value);
- event Approval(
- address indexed owner,
- address indexed spender,
- uint256 value
- );
-
- function transfer(address recipient, uint256 amount)
- external
- returns (bool);
-
- function transferFrom(
- address sender,
- address recipient,
- uint256 amount
- ) external returns (bool);
-}
-
-contract MarketPlaceKSM {
- struct Offer {
- uint256 idNFT;
- uint256 currencyCode;
- uint256 price;
- uint256 time;
- address idCollection;
- address userAddr;
- uint8 flagActive;
- }
- Offer[] public offers;
-
- mapping(address => mapping(uint256 => uint256)) public balanceKSM; // [userAddr] => [KSMs]
- mapping(address => mapping(uint256 => uint256)) public asks; // [buyer][idCollection][idNFT] => idOffer
-
- mapping(address => uint256[]) public asksbySeller; // [addressSeller] =>idOffer
-
- address escrow;
- address owner;
-
- constructor(address _escrow) {
- escrow = _escrow;
- owner = msg.sender;
- }
-
- function setowner(address _newEscrow) public onlyOwner {
- escrow = _newEscrow;
- }
-
- function setEscrow(address _newEscrow) public onlyOwner {
- escrow = _newEscrow;
- }
-
- modifier onlyEscrow() {
- require(msg.sender == escrow, "Only escrow can");
- _;
- }
-
- modifier onlyOwner() {
- require(msg.sender == owner, "Only owner can");
- _;
- }
-
- /**
- * Make bids (offers) to sell NFTs
- */
- function setAsk(
- uint256 _price,
- uint256 _currencyCode,
- address _idCollection,
- uint256 _idNFT,
- uint8 _active
- ) public {
- require(
- IERC721(_idCollection).ownerOf(_idNFT) == msg.sender,
- "Not right token owner"
- );
- uint256 offerID = asks[_idCollection][_idNFT];
- if (offers.length == 0 || offers[offerID].idCollection == address(0)) {
- offers.push(
- Offer(
- _idNFT,
- _currencyCode,
- _price,
- block.timestamp,
- _idCollection,
- msg.sender,
- _active
- )
- );
- asks[_idCollection][_idNFT] = offers.length - 1;
- asksbySeller[msg.sender].push(offers.length - 1);
- }
- //edit existing offer
- else {
- offers[asks[_idCollection][_idNFT]] = Offer(
- offers[asks[_idCollection][_idNFT]].idNFT,
- _currencyCode,
- _price,
- block.timestamp,
- offers[asks[_idCollection][_idNFT]].idCollection,
- msg.sender,
- _active
- );
- }
-
- IERC721(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
- }
-
- function deposit(
- uint256 _amount,
- uint256 _currencyCode,
- address _sender
- ) public onlyEscrow {
- balanceKSM[_sender][_currencyCode] =
- balanceKSM[_sender][_currencyCode] +
- _amount;
- }
-
- function buy(address _idCollection, uint256 _idNFT) public {
- Offer memory offer = offers[asks[_idCollection][_idNFT]];
- // 1. reduce balance
- balanceKSM[msg.sender][offer.currencyCode] =
- balanceKSM[msg.sender][offer.currencyCode] -
- offer.price;
- // 2. increase balance
- balanceKSM[offer.userAddr][offer.currencyCode] =
- balanceKSM[offer.userAddr][offer.currencyCode] +
- offer.price;
- // 3. close offer
- offers[asks[_idCollection][_idNFT]].flagActive = 0;
- // 4. transfer NFT to buyer
- IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
- }
-
- function withdraw(
- uint256 _amount,
- uint256 _currencyCode,
- address _sender
- ) public onlyEscrow returns (bool) {
- balanceKSM[_sender][_currencyCode] =
- balanceKSM[_sender][_currencyCode] -
- _amount;
- return true;
- }
-
- function escrowBalance(uint256 _currencyCode, address _sender)
- public
- view
- returns (uint256)
- {
- return balanceKSM[_sender][_currencyCode];
- }
-}
tests/src/eth/marketplace/MarketPlaceUNQ.abidiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceUNQ.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"asksbySeller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"address","name":"currencyCode","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"address","name":"idCollection","type":"address"},{"internalType":"address","name":"userAddr","type":"address"},{"internalType":"uint8","name":"flagActive","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"address","name":"_currencyCode","type":"address"},{"internalType":"address","name":"_idCollection","type":"address"},{"internalType":"uint256","name":"_idNFT","type":"uint256"},{"internalType":"uint8","name":"_active","type":"uint8"}],"name":"setAsk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setowner","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
tests/src/eth/marketplace/MarketPlaceUNQ.bindiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceUNQ.bin
+++ /dev/null
@@ -1 +0,0 @@
-608060405234801561001057600080fd5b50600380546001600160a01b03191633179055610f2d806100326000396000f3fe6080604052600436106100705760003560e01c806399f2d4eb1161004e57806399f2d4eb14610120578063b3ffb7601461014e578063cce7ec1314610161578063e22d4f7d1461017457600080fd5b8063592bd705146100755780637b1d6001146100975780638a72ea6a146100b7575b600080fd5b34801561008157600080fd5b50610095610090366004610d2b565b6101ac565b005b3480156100a357600080fd5b506100956100b2366004610e1b565b61021e565b3480156100c357600080fd5b506100d76100d2366004610e02565b61073d565b604080519788526001600160a01b03968716602089015287019490945260608601929092528316608085015290911660a083015260ff1660c082015260e0015b60405180910390f35b34801561012c57600080fd5b5061014061013b366004610d6c565b6107a0565b604051908152602001610117565b61009561015c366004610d98565b6107d1565b61009561016f366004610d6c565b610b0b565b34801561018057600080fd5b5061014061018f366004610d6c565b600160209081526000928352604080842090915290825290205481565b6003546001600160a01b031633146101fc5760405162461bcd60e51b815260206004820152600e60248201526d27b7363c9037bbb732b91031b0b760911b60448201526064015b60405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b6040516331a9108f60e11b81526004810183905233906001600160a01b03851690636352211e9060240160206040518083038186803b15801561026057600080fd5b505afa158015610274573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102989190610d4f565b6001600160a01b0316146102e65760405162461bcd60e51b81526020600482015260156024820152742737ba103934b3b43a103a37b5b2b71037bbb732b960591b60448201526064016101f3565b6001600160a01b038316600090815260016020908152604080832085845290915281205490541580610350575060006001600160a01b03166000828154811061033157610331610ec9565b60009182526020909120600460069092020101546001600160a01b0316145b15610542576040805160e0810182528481526001600160a01b03878116602083019081529282018981524260608401908152888316608085019081523360a0860190815260ff89811660c08801908152600080546001808201835582805299517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56360069092029182015599517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5648b0180546001600160a01b0319908116928b1692909217905596517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5658b015594517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5668a015592517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5678901805490961690871617909455517f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56890960180549151969094166001600160a81b031990911617600160a01b959092169490940217905590546104e99190610ea4565b6001600160a01b0385166000908152600160208181526040808420888552825280842094909455338352600290529181209054909161052791610ea4565b815460018101835560009283526020909220909101556106d3565b6040805160e0810182526001600160a01b038616600090815260016020908152838220878352905291822054825491928392909190811061058557610585610ec9565b6000918252602080832060069092029091015483526001600160a01b03808a168483015260408085018c9052426060860152908916835260018252808320888452909152812054815460809093019281106105e2576105e2610ec9565b60009182526020808320600460069093020191909101546001600160a01b039081168452338483015260ff871660409485015288168252600181528282208783529052908120548154811061063957610639610ec9565b60009182526020918290208351600690920201908155908201516001820180546001600160a01b039283166001600160a01b031991821617909155604084015160028401556060840151600384015560808401516004840180549184169190921617905560a08301516005909201805460c09094015160ff16600160a01b026001600160a81b031990941692909116919091179190911790555b6040516323b872dd60e01b81526001600160a01b038516906323b872dd9061070390339030908890600401610e80565b600060405180830381600087803b15801561071d57600080fd5b505af1158015610731573d6000803e3d6000fd5b50505050505050505050565b6000818154811061074d57600080fd5b60009182526020909120600690910201805460018201546002830154600384015460048501546005909501549395506001600160a01b03928316949193909291821691811690600160a01b900460ff1687565b600260205281600052604060002081815481106107bc57600080fd5b90600052602060002001600091509150505481565b6001600160a01b03841660009081526001602090815260408083208684529091528120548154829190811061080857610808610ec9565b60009182526020918290206040805160e0810182526006939093029091018054835260018101546001600160a01b03908116948401949094526002810154918301829052600381015460608401526004810154841660808401526005015492831660a0830152600160a01b90920460ff1660c08201529150821480156108a35750826001600160a01b031681602001516001600160a01b0316145b6109245760405162461bcd60e51b815260206004820152604660248201527f4e6f7420726967687420616d6f756e74206f722063757272656e63792073656e60448201527f742c206861766520746f20626520657175616c2063757272656e637920616e6460648201526520707269636560d01b608482015260a4016101f3565b80602001516001600160a01b03166323b872dd333084604001516040518463ffffffff1660e01b815260040161095c93929190610e80565b602060405180830381600087803b15801561097657600080fd5b505af115801561098a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ae9190610de0565b50602081015160a0820151604080840151905163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb90604401602060405180830381600087803b158015610a0957600080fd5b505af1158015610a1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a419190610de0565b506001600160a01b038516600090815260016020908152604080832087845290915281205481548291908110610a7957610a79610ec9565b906000526020600020906006020160050160146101000a81548160ff021916908360ff160217905550846001600160a01b03166323b872dd3033876040518463ffffffff1660e01b8152600401610ad293929190610e80565b600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b505050505050505050565b6001600160a01b038216600090815260016020908152604080832084845290915281205481548291908110610b4257610b42610ec9565b60009182526020918290206040805160e0810182526006939093029091018054835260018101546001600160a01b03908116948401949094526002810154918301829052600381015460608401526004810154841660808401526005015492831660a0830152600160a01b90920460ff1660c082015291503414610c225760405162461bcd60e51b815260206004820152603160248201527f4e6f7420726967687420616d6f756e7420554e512073656e742c206861766520604482015270746f20626520657175616c20707269636560781b60648201526084016101f3565b8060a001516001600160a01b03166108fc82604001519081150290604051600060405180830381858888f19350505050158015610c63573d6000803e3d6000fd5b506001600160a01b038316600090815260016020908152604080832085845290915281205481548291908110610c9b57610c9b610ec9565b906000526020600020906006020160050160146101000a81548160ff021916908360ff160217905550826001600160a01b03166323b872dd3033856040518463ffffffff1660e01b8152600401610cf493929190610e80565b600060405180830381600087803b158015610d0e57600080fd5b505af1158015610d22573d6000803e3d6000fd5b50505050505050565b600060208284031215610d3d57600080fd5b8135610d4881610edf565b9392505050565b600060208284031215610d6157600080fd5b8151610d4881610edf565b60008060408385031215610d7f57600080fd5b8235610d8a81610edf565b946020939093013593505050565b60008060008060808587031215610dae57600080fd5b8435610db981610edf565b9350602085013592506040850135610dd081610edf565b9396929550929360600135925050565b600060208284031215610df257600080fd5b81518015158114610d4857600080fd5b600060208284031215610e1457600080fd5b5035919050565b600080600080600060a08688031215610e3357600080fd5b853594506020860135610e4581610edf565b93506040860135610e5581610edf565b925060608601359150608086013560ff81168114610e7257600080fd5b809150509295509295909350565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600082821015610ec457634e487b7160e01b600052601160045260246000fd5b500390565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0381168114610ef457600080fd5b5056fea2646970667358221220fbf33eb1e471c428b86ad6d948b8699fd08a127b923d5fe27ec1e1ec5f59627564736f6c63430008070033
\ No newline at end of file
tests/src/eth/marketplace/MarketPlaceUNQ.soldiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceUNQ.sol
+++ /dev/null
@@ -1,163 +0,0 @@
-// SPDX-License-Identifier: Apache License
-pragma solidity >=0.8.0;
-
-interface IERC721 {
- event Transfer(
- address indexed from,
- address indexed to,
- uint256 indexed tokenId
- );
- event Approval(
- address indexed owner,
- address indexed approved,
- uint256 indexed tokenId
- );
-
- function ownerOf(uint256 tokenId) external view returns (address owner);
-
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) external;
-
- function debug(string memory value) external;
-}
-
-interface IERC20 {
- event Transfer(address indexed from, address indexed to, uint256 value);
- event Approval(
- address indexed owner,
- address indexed spender,
- uint256 value
- );
-
- function transfer(address recipient, uint256 amount)
- external
- returns (bool);
-
- function transferFrom(
- address sender,
- address recipient,
- uint256 amount
- ) external returns (bool);
-}
-
-contract MarketPlaceUNQ {
- struct Offer {
- uint256 idNFT;
- address currencyCode; //address of currency token, = address(0) for UNQ
- uint256 price;
- uint256 time;
- address idCollection;
- address userAddr;
- uint8 flagActive;
- }
- Offer[] public offers;
-
- mapping(address => mapping(uint256 => uint256)) public asks; // [idCollection][idNFT] => idOffer
-
- mapping(address => uint256[]) public asksbySeller; // [addressSeller] =>idOffer
-
- address owner;
-
- constructor() {
- owner = msg.sender;
- }
-
- function setowner(address _newOwner) public onlyOwner {
- owner = _newOwner;
- }
-
- modifier onlyOwner() {
- require(msg.sender == owner, "Only owner can");
- _;
- }
-
- /**
- * Make bids (offers) to sell NFTs
- */
- function setAsk(
- uint256 _price,
- address _currencyCode,
- address _idCollection,
- uint256 _idNFT,
- uint8 _active
- ) public {
- require(
- IERC721(_idCollection).ownerOf(_idNFT) == msg.sender,
- "Not right token owner"
- );
- uint256 offerID = asks[_idCollection][_idNFT];
- if (offers.length == 0 || offers[offerID].idCollection == address(0)) {
- offers.push(
- Offer(
- _idNFT,
- _currencyCode,
- _price,
- block.timestamp,
- _idCollection,
- msg.sender,
- _active
- )
- );
- asks[_idCollection][_idNFT] = offers.length - 1;
- asksbySeller[msg.sender].push(offers.length - 1);
- }
- //edit existing offer
- else {
- offers[asks[_idCollection][_idNFT]] = Offer(
- offers[asks[_idCollection][_idNFT]].idNFT,
- _currencyCode,
- _price,
- block.timestamp,
- offers[asks[_idCollection][_idNFT]].idCollection,
- msg.sender,
- _active
- );
- }
-
- IERC721(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
- }
-
- function buy(address _idCollection, uint256 _idNFT) public payable {
- //buing for UNQ like as ethers
- Offer memory offer = offers[asks[_idCollection][_idNFT]];
- //1. check sent amount and send to seller
- require(
- msg.value == offer.price,
- "Not right amount UNQ sent, have to be equal price"
- );
- payable(offer.userAddr).transfer(offer.price);
- // 2. close offer
- offers[asks[_idCollection][_idNFT]].flagActive = 0;
- // 3. transfer NFT to buyer
- IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
- }
-
- function buy(
- address _idCollection,
- uint256 _idNFT,
- address _currencyCode,
- uint256 _amount
- ) public payable {
- Offer memory offer = offers[asks[_idCollection][_idNFT]];
- //1. check sent amount and transfer from buyer to seller
- require(
- offer.price == _amount && offer.currencyCode == _currencyCode,
- "Not right amount or currency sent, have to be equal currency and price"
- );
- // !!! transfer have to be approved to marketplace!
- IERC20(offer.currencyCode).transferFrom(
- msg.sender,
- address(this),
- offer.price
- );
- //to not disclojure buyer's address
- IERC20(offer.currencyCode).transfer(offer.userAddr, offer.price);
- // 2. close offer
- offers[asks[_idCollection][_idNFT]].flagActive = 0;
- // 3. transfer NFT to buyer
- IERC721(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
- }
-}
tests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth--- a/tests/src/eth/marketplace/marketplace.test.ts
+++ b/tests/src/eth/marketplace/marketplace.test.ts
@@ -1,8 +1,8 @@
import {readFile} from 'fs/promises';
import {getBalanceSingle, transferBalanceExpectSuccess} from '../../substrate/get-balance';
import privateKey from '../../substrate/privateKey';
-import {createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';
-import {collectionIdToAddress, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase} from '../util/helpers';
+import {addToAllowListExpectSuccess, confirmSponsorshipExpectSuccess, createCollectionExpectSuccess, createFungibleItemExpectSuccess, createItemExpectSuccess, getTokenOwner, setCollectionSponsorExpectSuccess, transferExpectSuccess, transferFromExpectSuccess} from '../../util/helpers';
+import {collectionIdToAddress, contractHelpers, createEthAccountWithBalance, executeEthTxOnSub, GAS_ARGS, itWeb3, subToEth, subToEthLowercase, transferBalanceToEth} from '../util/helpers';
import {evmToAddress} from '@polkadot/util-crypto';
import nonFungibleAbi from '../nonFungibleAbi.json';
import fungibleAbi from '../fungibleAbi.json';
@@ -12,34 +12,39 @@
describe('Matcher contract usage', () => {
itWeb3('With UNQ', async ({api, web3}) => {
+ const alice = privateKey('//Alice');
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+ const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
const matcherOwner = await createEthAccountWithBalance(api, web3);
- const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {
+ const helpers = contractHelpers(web3, matcherOwner);
+
+ const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
});
- const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString()}).send({from: matcherOwner});
+ const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments:[matcherOwner]}).send({from: matcherOwner});
- const alice = privateKey('//Alice');
- const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
+ await transferBalanceToEth(api, alice, matcher.options.address);
+ await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
- const seller = privateKey('//Bob');
+ await transferBalanceToEth(api, alice, subToEth(alice.address));
+ await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId);
+ const seller = privateKey('//Seller/' + Date.now());
+ await addToAllowListExpectSuccess(alice, collectionId, {Ethereum:subToEth(seller.address)});
+ await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
+
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
- // To transfer item to matcher it first needs to be transfered to EVM account of bob
+ // To transfer item to matcher it first needs to be transfered to EVM account of seller
await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
- // Fees will be paid from EVM account, so we should have some balance here
- await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);
- await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);
-
- // Token is owned by seller initially
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
// Ask
{
await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));
- await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, '0x0000000000000000000000000000000000000000', evmCollection.options.address, tokenId, 1));
+ await executeEthTxOnSub(api, seller, matcher, m => m.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, tokenId));
}
// Token is transferred to matcher
@@ -48,14 +53,12 @@
// Buy
{
const sellerBalanceBeforePurchase = await getBalanceSingle(api, seller.address);
- // There is two functions named 'buy', so we should provide full signature
- await executeEthTxOnSub(api, alice, matcher, m => m['buy(address,uint256)'](evmCollection.options.address, tokenId), {value: PRICE});
+ await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId), {value: PRICE});
expect(await getBalanceSingle(api, seller.address) - sellerBalanceBeforePurchase === PRICE);
}
// Token is transferred to evm account of alice
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(alice.address)});
-
// Transfer token to substrate side of alice
await transferFromExpectSuccess(collectionId, tokenId, alice, {Ethereum: subToEth(alice.address)}, {Substrate: alice.address});
@@ -64,13 +67,14 @@
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Substrate: alice.address});
});
- itWeb3('With custom ERC20', async ({api, web3}) => {
+ // selling for custom tokens excluded from release
+ itWeb3.skip('With custom ERC20', async ({api, web3}) => {
const matcherOwner = await createEthAccountWithBalance(api, web3);
- const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceUNQ.abi`)).toString()), undefined, {
+ const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
});
- const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlaceUNQ.bin`)).toString()}).send({from: matcherOwner});
+ const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});
const alice = privateKey('//Alice');
const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
@@ -129,55 +133,59 @@
});
itWeb3('With escrow', async ({api, web3}) => {
+ const alice = privateKey('//Alice');
+ const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+ const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId));
const matcherOwner = await createEthAccountWithBalance(api, web3);
+ const helpers = contractHelpers(web3, matcherOwner);
const escrow = await createEthAccountWithBalance(api, web3);
- const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlaceKSM.abi`)).toString()), undefined, {
+
+ const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
});
- const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlaceKSM.bin`)).toString(), arguments: [escrow]}).send({from: matcherOwner});
+ const matcher = await matcherContract.deploy({data: (await readFile(`${__dirname}/MarketPlace.bin`)).toString(), arguments: [matcherOwner]}).send({from: matcherOwner});
+ await matcher.methods.setEscrow(escrow).send({from: matcherOwner});
- const ksmToken = 11;
+ await transferBalanceToEth(api, alice, matcher.options.address);
+ await helpers.methods.toggleSponsoring(matcher.options.address, true).send({from: matcherOwner});
- const alice = privateKey('//Alice');
- const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
- const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collectionId), {from: matcherOwner});
+ await transferBalanceToEth(api, alice, subToEth(alice.address));
+ await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+ await confirmSponsorshipExpectSuccess(collectionId);
- const seller = privateKey('//Bob');
+ const seller = privateKey('//Seller/' + Date.now());
+ await helpers.methods.toggleAllowed(matcher.options.address, subToEth(seller.address), true).send({from: matcherOwner});
+ await addToAllowListExpectSuccess(alice, collectionId, evmToAddress(subToEth(seller.address)));
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', seller.address);
- // To transfer item to matcher it first needs to be transfered to EVM account of bob
+ // To transfer item to matcher it first needs to be transfered to EVM account of seller
await transferExpectSuccess(collectionId, tokenId, seller, {Ethereum: subToEth(seller.address)});
- // Fees will be paid from EVM account, so we should have some balance here
- await transferBalanceExpectSuccess(api, seller, evmToAddress(subToEth(seller.address)), 10n ** 18n);
- await transferBalanceExpectSuccess(api, alice, evmToAddress(subToEth(alice.address)), 10n ** 18n);
-
- // Token is owned by seller initially
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: subToEthLowercase(seller.address)});
// Ask
{
await executeEthTxOnSub(api, seller, evmCollection, m => m.approve(matcher.options.address, tokenId));
- await executeEthTxOnSub(api, seller, matcher, m => m.setAsk(PRICE, ksmToken, evmCollection.options.address, tokenId, 1));
+ await executeEthTxOnSub(api, seller, matcher, m => m.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, tokenId));
}
// Token is transferred to matcher
expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
// Give buyer KSM
- await matcher.methods.deposit(PRICE, ksmToken, subToEth(alice.address)).send({from: escrow});
+ await matcher.methods.depositKSM(PRICE, subToEth(alice.address)).send({from: escrow});
// Buy
{
- expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal('0');
- expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal(PRICE.toString());
+ expect(await matcher.methods.balanceKSM(subToEth(seller.address)).call()).to.be.equal('0');
+ expect(await matcher.methods.balanceKSM(subToEth(alice.address)).call()).to.be.equal(PRICE.toString());
- await executeEthTxOnSub(api, alice, matcher, m => m.buy(evmCollection.options.address, tokenId));
+ await executeEthTxOnSub(api, alice, matcher, m => m.buyKSM(evmCollection.options.address, tokenId, subToEth(alice.address), subToEth(alice.address)));
// Price is removed from buyer balance, and added to seller
- expect(await matcher.methods.escrowBalance(ksmToken, subToEth(alice.address)).call()).to.be.equal('0');
- expect(await matcher.methods.escrowBalance(ksmToken, subToEth(seller.address)).call()).to.be.equal(PRICE.toString());
+ expect(await matcher.methods.balanceKSM(subToEth(alice.address)).call()).to.be.equal('0');
+ expect(await matcher.methods.balanceKSM(subToEth(seller.address)).call()).to.be.equal(PRICE.toString());
}
// Token is transferred to evm account of alice
tests/src/eth/util/helpers.tsdiffbeforeafterboth--- a/tests/src/eth/util/helpers.ts
+++ b/tests/src/eth/util/helpers.ts
@@ -228,6 +228,18 @@
return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, ...GAS_ARGS});
}
+/**
+ * Execute ethereum method call using substrate account
+ * @param to target contract
+ * @param mkTx - closure, receiving `contract.methods`, and returning method call,
+ * to be used as following (assuming `to` = erc20 contract):
+ * `m => m.transfer(to, amount)`
+ *
+ * # Example
+ * ```ts
+ * executeEthTxOnSub(api, alice, erc20Contract, m => m.transfer(target, amount));
+ * ```
+ */
export async function executeEthTxOnSub(api: ApiPromise, from: IKeyringPair, to: any, mkTx: (methods: any) => any, {value = 0}: {value?: bigint | number} = { }) {
const tx = api.tx.evm.call(
subToEth(from.address),
@@ -246,6 +258,11 @@
return (await getBalance(api, [evmToAddress(address)]))[0];
}
+/**
+ * Measure how much gas given closure consumes
+ *
+ * @param user which user balance will be checked
+ */
export async function recordEthFee(api: ApiPromise, user: string, call: () => Promise<any>): Promise<bigint> {
const before = await ethBalanceViaSub(api, user);
tests/src/limits.test.tsdiffbeforeafterboth--- a/tests/src/limits.test.ts
+++ b/tests/src/limits.test.ts
@@ -78,7 +78,7 @@
});
});
-describe('Sponsor timeout (NFT)', () => {
+describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
let charlie: IKeyringPair;
@@ -91,7 +91,7 @@
});
});
- it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {
+ it.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {
const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
@@ -141,7 +141,7 @@
});
});
-describe('Sponsor timeout (Fungible)', () => {
+describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
let charlie: IKeyringPair;
@@ -208,7 +208,7 @@
});
});
-describe('Sponsor timeout (ReFungible)', () => {
+describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
let charlie: IKeyringPair;
tests/src/overflow.test.tsdiffbeforeafterboth--- a/tests/src/overflow.test.ts
+++ b/tests/src/overflow.test.ts
@@ -13,7 +13,7 @@
chai.use(chaiAsPromised);
const expect = chai.expect;
-describe('Integration Test fungible overflows', () => {
+describe.skip('Integration Test fungible overflows', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
let charlie: IKeyringPair;
tests/src/pallet-presence.test.tsdiffbeforeafterboth--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -39,7 +39,6 @@
'nonfungible',
'refungible',
'scheduler',
- 'nftpayment',
'charging',
];
tests/src/setOffchainSchema.test.tsdiffbeforeafterboth--- a/tests/src/setOffchainSchema.test.ts
+++ b/tests/src/setOffchainSchema.test.ts
@@ -40,7 +40,7 @@
await setOffchainSchemaExpectSuccess(alice, collectionId, DATA);
const collection = await queryCollectionExpectSuccess(api, collectionId);
- expect(collection.offchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));
+ expect('0x' + Buffer.from(collection.offchainSchema).toString('hex')).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));
});
});
@@ -51,7 +51,7 @@
await setOffchainSchemaExpectSuccess(bob, collectionId, DATA);
const collection = await queryCollectionExpectSuccess(api, collectionId);
- expect(collection.offchainSchema).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));
+ expect('0x' + Buffer.from(collection.offchainSchema).toString('hex')).to.be.equal('0x' + Buffer.from(DATA).toString('hex'));
});
});
});
tests/src/transfer.test.tsdiffbeforeafterboth--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -20,6 +20,10 @@
transferExpectSuccess,
addCollectionAdminExpectSuccess,
getCreatedCollectionCount,
+ toSubstrateAddress,
+ getTokenOwner,
+ normalizeAccountId,
+ getBalance as getTokenBalance,
} from './util/helpers';
let alice: IKeyringPair;
@@ -231,3 +235,61 @@
);
});
});
+
+describe('Zero value transfer(From)', () => {
+ before(async () => {
+ await usingApi(async () => {
+ alice = privateKey('//Alice');
+ bob = privateKey('//Bob');
+ });
+ });
+
+ it('NFT', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const nftCollectionId = await createCollectionExpectSuccess();
+ const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
+
+ const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), nftCollectionId, newNftTokenId, 0);
+ await submitTransactionAsync(alice, transferTx);
+ const address = normalizeAccountId(await getTokenOwner(api, nftCollectionId, newNftTokenId));
+
+ expect(toSubstrateAddress(address)).to.be.equal(alice.address);
+ });
+ });
+
+ it('RFT', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
+ const balanceBeforeAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);
+ const balanceBeforeBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);
+
+ const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), reFungibleCollectionId, newReFungibleTokenId, 0);
+ await submitTransactionAsync(alice, transferTx);
+
+ const balanceAfterAlice = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(alice), newReFungibleTokenId);
+ const balanceAfterBob = await getTokenBalance(api, reFungibleCollectionId, normalizeAccountId(bob), newReFungibleTokenId);
+
+ expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);
+ expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);
+ });
+ });
+
+ it('Fungible', async () => {
+ await usingApi(async (api: ApiPromise) => {
+ const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
+ const balanceBeforeAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);
+ const balanceBeforeBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);
+
+ const transferTx = api.tx.nft.transfer(normalizeAccountId(bob), fungibleCollectionId, newFungibleTokenId, 0);
+ await submitTransactionAsync(alice, transferTx);
+
+ const balanceAfterAlice = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(alice), newFungibleTokenId);
+ const balanceAfterBob = await getTokenBalance(api, fungibleCollectionId, normalizeAccountId(bob), newFungibleTokenId);
+
+ expect((balanceBeforeAlice)).to.be.equal(balanceAfterAlice);
+ expect((balanceBeforeBob)).to.be.equal(balanceAfterBob);
+ });
+ });
+});
\ No newline at end of file
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -905,11 +905,12 @@
await usingApi(async (api: ApiPromise) => {
const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);
const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;
- if (events && Array.isArray(events)) {
- const result = getCreateCollectionResult(events);
- // tslint:disable-next-line:no-unused-expression
- expect(result.success).to.be.false;
- }
+ const result = getGenericResult(events);
+ // if (events && Array.isArray(events)) {
+ // const result = getCreateCollectionResult(events);
+ // tslint:disable-next-line:no-unused-expression
+ expect(result.success).to.be.false;
+ //}
});
}