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.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.78#![cfg_attr(not(feature = "std"), no_std)]9// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.10#![recursion_limit = "1024"]11#![allow(clippy::from_over_into, clippy::identity_op)]12#![allow(clippy::fn_to_numeric_cast_with_truncation)]13// Make the WASM binary available.14#[cfg(feature = "std")]15include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1617use sp_api::impl_runtime_apis;18use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};19// #[cfg(any(feature = "std", test))]20// pub use sp_runtime::BuildStorage;2122use sp_runtime::{23 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,24 traits::{25 AccountIdLookup, ConvertInto, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify,26 AccountIdConversion,27 },28 transaction_validity::{TransactionSource, TransactionValidity},29 ApplyExtrinsicResult, MultiSignature,30};3132use sp_std::prelude::*;3334#[cfg(feature = "std")]35use sp_version::NativeVersion;36use sp_version::RuntimeVersion;37pub use pallet_transaction_payment::{38 Multiplier, TargetedFeeAdjustment, FeeDetails, RuntimeDispatchInfo,39};40// A few exports that help ease life for downstream crates.41pub use pallet_balances::Call as BalancesCall;42pub use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping, Runner};43pub use frame_support::{44 construct_runtime, match_type,45 dispatch::DispatchResult,46 PalletId, parameter_types, StorageValue, ConsensusEngineId,47 traits::{48 Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem,49 LockIdentifier, OnUnbalanced, Randomness, FindAuthor,50 },51 weights::{52 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},53 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,54 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients,55 },56};57use nft_data_structs::*;58// use pallet_contracts::weights::WeightInfo;59// #[cfg(any(feature = "std", test))]60use frame_system::{61 self as system, EnsureRoot, EnsureSigned,62 limits::{BlockWeights, BlockLength},63};64use sp_arithmetic::{65 traits::{BaseArithmetic, Unsigned},66};67use smallvec::smallvec;68use codec::{Encode, Decode};69use pallet_evm::{Account as EVMAccount, FeeCalculator, OnMethodCall};70use fp_rpc::TransactionStatus;71use sp_core::crypto::Public;72use sp_runtime::{73 traits::{Dispatchable, PostDispatchInfoOf},74 transaction_validity::TransactionValidityError,75};7677// pub use pallet_timestamp::Call as TimestampCall;78pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;7980// Polkadot imports81use pallet_xcm::XcmPassthrough;82use polkadot_parachain::primitives::Sibling;83use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};84use xcm_builder::{85 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,86 EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,87 ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,88 SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,89 SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,90};91use xcm_executor::{Config, XcmExecutor};9293// mod chain_extension;94// use crate::chain_extension::{NFTExtension, Imbalance};9596/// An index to a block.97pub type BlockNumber = u32;9899/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.100pub type Signature = MultiSignature;101102/// Some way of identifying an account on the chain. We intentionally make it equivalent103/// to the public key of our transaction signing scheme.104pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;105106pub type CrossAccountId = pallet_common::account::BasicCrossAccountId<Runtime>;107108/// The type for looking up accounts. We don't expect more than 4 billion of them, but you109/// never know...110pub type AccountIndex = u32;111112/// Balance of an account.113pub type Balance = u128;114115/// Index of a transaction in the chain.116pub type Index = u32;117118/// A hash of some data used by the chain.119pub type Hash = sp_core::H256;120121/// Digest item type.122pub type DigestItem = generic::DigestItem<Hash>;123124/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know125/// the specifics of the runtime. They can then be made to be agnostic over specific formats126/// of data like extrinsics, allowing for them to continue syncing the network through upgrades127/// to even the core data structures.128pub mod opaque {129 use super::*;130131 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;132133 /// Opaque block type.134 pub type Block = generic::Block<Header, UncheckedExtrinsic>;135136 pub type SessionHandlers = ();137138 impl_opaque_keys! {139 pub struct SessionKeys {140 pub aura: Aura,141 }142 }143}144145/// This runtime version.146pub const VERSION: RuntimeVersion = RuntimeVersion {147 spec_name: create_runtime_str!("opal"),148 impl_name: create_runtime_str!("opal"),149 authoring_version: 1,150 spec_version: 912202,151 impl_version: 1,152 apis: RUNTIME_API_VERSIONS,153 transaction_version: 1,154};155156pub const MILLISECS_PER_BLOCK: u64 = 12000;157158pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;159160// These time units are defined in number of blocks.161pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);162pub const HOURS: BlockNumber = MINUTES * 60;163pub const DAYS: BlockNumber = HOURS * 24;164165parameter_types! {166 pub const DefaultSponsoringRateLimit: BlockNumber = 1 * DAYS;167}168169#[derive(codec::Encode, codec::Decode)]170pub enum XCMPMessage<XAccountId, XBalance> {171 /// Transfer tokens to the given account from the Parachain account.172 TransferToken(XAccountId, XBalance),173}174175/// The version information used to identify this runtime when compiled natively.176#[cfg(feature = "std")]177pub fn native_version() -> NativeVersion {178 NativeVersion {179 runtime_version: VERSION,180 can_author_with: Default::default(),181 }182}183184type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;185186pub struct DealWithFees;187impl OnUnbalanced<NegativeImbalance> for DealWithFees {188 fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) {189 if let Some(fees) = fees_then_tips.next() {190 // for fees, 100% to treasury191 let mut split = fees.ration(100, 0);192 if let Some(tips) = fees_then_tips.next() {193 // for tips, if any, 100% to treasury194 tips.ration_merge_into(100, 0, &mut split);195 }196 Treasury::on_unbalanced(split.0);197 // Author::on_unbalanced(split.1);198 }199 }200}201202/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers.203/// This is used to limit the maximal weight of a single extrinsic.204const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);205/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used206/// by Operational extrinsics.207const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);208/// We allow for 2 seconds of compute with a 6 second average block time.209const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2;210211parameter_types! {212 pub const BlockHashCount: BlockNumber = 2400;213 pub RuntimeBlockLength: BlockLength =214 BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);215 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);216 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;217 pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()218 .base_block(BlockExecutionWeight::get())219 .for_class(DispatchClass::all(), |weights| {220 weights.base_extrinsic = ExtrinsicBaseWeight::get();221 })222 .for_class(DispatchClass::Normal, |weights| {223 weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);224 })225 .for_class(DispatchClass::Operational, |weights| {226 weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);227 // Operational transactions have some extra reserved space, so that they228 // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.229 weights.reserved = Some(230 MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT231 );232 })233 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)234 .build_or_panic();235 pub const Version: RuntimeVersion = VERSION;236 pub const SS58Prefix: u8 = 42;237}238239parameter_types! {240 pub const ChainId: u64 = 8888;241}242243pub struct FixedFee;244impl FeeCalculator for FixedFee {245 fn min_gas_price() -> U256 {246 1.into()247 }248}249250impl pallet_evm::Config for Runtime {251 type BlockGasLimit = BlockGasLimit;252 type FeeCalculator = FixedFee;253 type GasWeightMapping = ();254 type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;255 type CallOrigin = EnsureAddressTruncated;256 type WithdrawOrigin = EnsureAddressTruncated;257 type AddressMapping = HashedAddressMapping<Self::Hashing>;258 type Precompiles = ();259 type Currency = Balances;260 type Event = Event;261 type OnMethodCall = (262 pallet_evm_migration::OnMethodCall<Self>,263 pallet_nft::NftErcSupport<Self>,264 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,265 );266 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;267 type ChainId = ChainId;268 type Runner = pallet_evm::runner::stack::Runner<Self>;269 type OnChargeTransaction = pallet_evm_transaction_payment::OnChargeTransaction<Self>;270 type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack<Self>;271 type FindAuthor = EthereumFindAuthor<Aura>;272}273274impl pallet_evm_migration::Config for Runtime {275 type WeightInfo = pallet_evm_migration::weights::SubstrateWeight<Self>;276}277278pub struct EthereumFindAuthor<F>(core::marker::PhantomData<F>);279impl<F: FindAuthor<u32>> FindAuthor<H160> for EthereumFindAuthor<F> {280 fn find_author<'a, I>(digests: I) -> Option<H160>281 where282 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,283 {284 if let Some(author_index) = F::find_author(digests) {285 let authority_id = Aura::authorities()[author_index as usize].clone();286 return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24]));287 }288 None289 }290}291292parameter_types! {293 pub BlockGasLimit: U256 = U256::from(u32::max_value());294}295296impl pallet_ethereum::Config for Runtime {297 type Event = Event;298 type StateRoot = pallet_ethereum::IntermediateStateRoot;299 type EvmSubmitLog = pallet_evm::Pallet<Self>;300}301302impl pallet_randomness_collective_flip::Config for Runtime {}303304impl system::Config for Runtime {305 /// The data to be stored in an account.306 type AccountData = pallet_balances::AccountData<Balance>;307 /// The identifier used to distinguish between accounts.308 type AccountId = AccountId;309 /// The basic call filter to use in dispatchable.310 type BaseCallFilter = Everything;311 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).312 type BlockHashCount = BlockHashCount;313 /// The maximum length of a block (in bytes).314 type BlockLength = RuntimeBlockLength;315 /// The index type for blocks.316 type BlockNumber = BlockNumber;317 /// The weight of the overhead invoked on the block import process, independent of the extrinsics included in that block.318 type BlockWeights = RuntimeBlockWeights;319 /// The aggregated dispatch type that is available for extrinsics.320 type Call = Call;321 /// The weight of database operations that the runtime can invoke.322 type DbWeight = RocksDbWeight;323 /// The ubiquitous event type.324 type Event = Event;325 /// The type for hashing blocks and tries.326 type Hash = Hash;327 /// The hashing algorithm used.328 type Hashing = BlakeTwo256;329 /// The header type.330 type Header = generic::Header<BlockNumber, BlakeTwo256>;331 /// The index type for storing how many extrinsics an account has signed.332 type Index = Index;333 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.334 type Lookup = AccountIdLookup<AccountId, ()>;335 /// What to do if an account is fully reaped from the system.336 type OnKilledAccount = ();337 /// What to do if a new account is created.338 type OnNewAccount = ();339 type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;340 /// The ubiquitous origin type.341 type Origin = Origin;342 /// This type is being generated by `construct_runtime!`.343 type PalletInfo = PalletInfo;344 /// This is used as an identifier of the chain. 42 is the generic substrate prefix.345 type SS58Prefix = SS58Prefix;346 /// Weight information for the extrinsics of this pallet.347 type SystemWeightInfo = system::weights::SubstrateWeight<Self>;348 /// Version of the runtime.349 type Version = Version;350}351352parameter_types! {353 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;354}355356impl pallet_timestamp::Config for Runtime {357 /// A timestamp: milliseconds since the unix epoch.358 type Moment = u64;359 type OnTimestampSet = ();360 type MinimumPeriod = MinimumPeriod;361 type WeightInfo = ();362}363364parameter_types! {365 // pub const ExistentialDeposit: u128 = 500;366 pub const ExistentialDeposit: u128 = 0;367 pub const MaxLocks: u32 = 50;368}369370impl pallet_balances::Config for Runtime {371 type MaxLocks = MaxLocks;372 type MaxReserves = ();373 type ReserveIdentifier = [u8; 8];374 /// The type for recording an account's balance.375 type Balance = Balance;376 /// The ubiquitous event type.377 type Event = Event;378 type DustRemoval = Treasury;379 type ExistentialDeposit = ExistentialDeposit;380 type AccountStore = System;381 type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;382}383384pub const MICROUNIQUE: Balance = 1_000_000_000;385pub const MILLIUNIQUE: Balance = 1_000 * MICROUNIQUE;386pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE;387pub const UNIQUE: Balance = 100 * CENTIUNIQUE;388389pub const fn deposit(items: u32, bytes: u32) -> Balance {390 items as Balance * 15 * CENTIUNIQUE + (bytes as Balance) * 6 * CENTIUNIQUE391}392393/*394parameter_types! {395 pub TombstoneDeposit: Balance = deposit(396 1,397 sp_std::mem::size_of::<pallet_contracts::Pallet<Runtime>> as u32,398 );399 pub DepositPerContract: Balance = TombstoneDeposit::get();400 pub const DepositPerStorageByte: Balance = deposit(0, 1);401 pub const DepositPerStorageItem: Balance = deposit(1, 0);402 pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);403 pub const SurchargeReward: Balance = 150 * MILLIUNIQUE;404 pub const SignedClaimHandicap: u32 = 2;405 pub const MaxDepth: u32 = 32;406 pub const MaxValueSize: u32 = 16 * 1024;407 pub const MaxCodeSize: u32 = 1024 * 1024 * 25; // 25 Mb408 // The lazy deletion runs inside on_initialize.409 pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *410 RuntimeBlockWeights::get().max_block;411 // The weight needed for decoding the queue should be less or equal than a fifth412 // of the overall weight dedicated to the lazy deletion.413 pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (414 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -415 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)416 )) / 5) as u32;417 pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();418}419420impl pallet_contracts::Config for Runtime {421 type Time = Timestamp;422 type Randomness = RandomnessCollectiveFlip;423 type Currency = Balances;424 type Event = Event;425 type RentPayment = ();426 type SignedClaimHandicap = SignedClaimHandicap;427 type TombstoneDeposit = TombstoneDeposit;428 type DepositPerContract = DepositPerContract;429 type DepositPerStorageByte = DepositPerStorageByte;430 type DepositPerStorageItem = DepositPerStorageItem;431 type RentFraction = RentFraction;432 type SurchargeReward = SurchargeReward;433 type WeightPrice = pallet_transaction_payment::Pallet<Self>;434 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;435 type ChainExtension = NFTExtension;436 type DeletionQueueDepth = DeletionQueueDepth;437 type DeletionWeightLimit = DeletionWeightLimit;438 type Schedule = Schedule;439 type CallStack = [pallet_contracts::Frame<Self>; 31];440}441*/442443parameter_types! {444 pub const TransactionByteFee: Balance = 501 * MICROUNIQUE; // Targeting 0.1 Unique per NFT transfer445 /// This value increases the priority of `Operational` transactions by adding446 /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.447 pub const OperationalFeeMultiplier: u8 = 5;448}449450/// Linear implementor of `WeightToFeePolynomial`451pub struct LinearFee<T>(sp_std::marker::PhantomData<T>);452453impl<T> WeightToFeePolynomial for LinearFee<T>454where455 T: BaseArithmetic + From<u32> + Copy + Unsigned,456{457 type Balance = T;458459 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {460 smallvec!(WeightToFeeCoefficient {461 coeff_integer: 146_700u32.into(), // Targeting 0.1 Unique per NFT transfer462 coeff_frac: Perbill::zero(),463 negative: false,464 degree: 1,465 })466 }467}468469impl pallet_transaction_payment::Config for Runtime {470 type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, DealWithFees>;471 type TransactionByteFee = TransactionByteFee;472 type OperationalFeeMultiplier = OperationalFeeMultiplier;473 type WeightToFee = LinearFee<Balance>;474 type FeeMultiplierUpdate = ();475}476477parameter_types! {478 pub const ProposalBond: Permill = Permill::from_percent(5);479 pub const ProposalBondMinimum: Balance = 1 * UNIQUE;480 pub const SpendPeriod: BlockNumber = 5 * MINUTES;481 pub const Burn: Permill = Permill::from_percent(0);482 pub const TipCountdown: BlockNumber = 1 * DAYS;483 pub const TipFindersFee: Percent = Percent::from_percent(20);484 pub const TipReportDepositBase: Balance = 1 * UNIQUE;485 pub const DataDepositPerByte: Balance = 1 * CENTIUNIQUE;486 pub const BountyDepositBase: Balance = 1 * UNIQUE;487 pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;488 pub const TreasuryModuleId: PalletId = PalletId(*b"py/trsry");489 pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;490 pub const MaximumReasonLength: u32 = 16384;491 pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);492 pub const BountyValueMinimum: Balance = 5 * UNIQUE;493 pub const MaxApprovals: u32 = 100;494}495496impl pallet_treasury::Config for Runtime {497 type PalletId = TreasuryModuleId;498 type Currency = Balances;499 type ApproveOrigin = EnsureRoot<AccountId>;500 type RejectOrigin = EnsureRoot<AccountId>;501 type Event = Event;502 type OnSlash = ();503 type ProposalBond = ProposalBond;504 type ProposalBondMinimum = ProposalBondMinimum;505 type SpendPeriod = SpendPeriod;506 type Burn = Burn;507 type BurnDestination = ();508 type SpendFunds = ();509 type WeightInfo = pallet_treasury::weights::SubstrateWeight<Self>;510 type MaxApprovals = MaxApprovals;511}512513impl pallet_sudo::Config for Runtime {514 type Event = Event;515 type Call = Call;516}517518parameter_types! {519 pub const MinVestedTransfer: Balance = 10 * UNIQUE;520}521522impl pallet_vesting::Config for Runtime {523 type Event = Event;524 type Currency = Balances;525 type BlockNumberToBalance = ConvertInto;526 type MinVestedTransfer = MinVestedTransfer;527 type WeightInfo = ();528 const MAX_VESTING_SCHEDULES: u32 = 28;529}530531parameter_types! {532 pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;533 pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;534}535536impl cumulus_pallet_parachain_system::Config for Runtime {537 type Event = Event;538 type OnValidationData = ();539 type SelfParaId = parachain_info::Pallet<Self>;540 // type DownwardMessageHandlers = cumulus_primitives_utility::UnqueuedDmpAsParent<541 // MaxDownwardMessageWeight,542 // XcmExecutor<XcmConfig>,543 // Call,544 // >;545 type OutboundXcmpMessageSource = XcmpQueue;546 type DmpMessageHandler = DmpQueue;547 type ReservedDmpWeight = ReservedDmpWeight;548 type ReservedXcmpWeight = ReservedXcmpWeight;549 type XcmpMessageHandler = XcmpQueue;550}551552impl parachain_info::Config for Runtime {}553554impl cumulus_pallet_aura_ext::Config for Runtime {}555556parameter_types! {557 pub const RelayLocation: MultiLocation = MultiLocation::parent();558 pub const RelayNetwork: NetworkId = NetworkId::Polkadot;559 pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();560 pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();561}562563/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used564/// when determining ownership of accounts for asset transacting and when attempting to use XCM565/// `Transact` in order to determine the dispatch Origin.566pub type LocationToAccountId = (567 // The parent (Relay-chain) origin converts to the default `AccountId`.568 ParentIsDefault<AccountId>,569 // Sibling parachain origins convert to AccountId via the `ParaId::into`.570 SiblingParachainConvertsVia<Sibling, AccountId>,571 // Straight up local `AccountId32` origins just alias directly to `AccountId`.572 AccountId32Aliases<RelayNetwork, AccountId>,573);574575/// Means for transacting assets on this chain.576pub type LocalAssetTransactor = CurrencyAdapter<577 // Use this currency:578 Balances,579 // Use this currency when it is a fungible asset matching the given location or name:580 IsConcrete<RelayLocation>,581 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:582 LocationToAccountId,583 // Our chain's account ID type (we can't get away without mentioning it explicitly):584 AccountId,585 // We don't track any teleports.586 (),587>;588589/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,590/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can591/// biases the kind of local `Origin` it will become.592pub type XcmOriginToTransactDispatchOrigin = (593 // Sovereign account converter; this attempts to derive an `AccountId` from the origin location594 // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for595 // foreign chains who want to have a local sovereign account on this chain which they control.596 SovereignSignedViaLocation<LocationToAccountId, Origin>,597 // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when598 // recognised.599 RelayChainAsNative<RelayOrigin, Origin>,600 // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when601 // recognised.602 SiblingParachainAsNative<cumulus_pallet_xcm::Origin, Origin>,603 // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a604 // transaction from the Root origin.605 ParentAsSuperuser<Origin>,606 // Native signed account converter; this just converts an `AccountId32` origin into a normal607 // `Origin::Signed` origin of the same 32-byte value.608 SignedAccountId32AsNative<RelayNetwork, Origin>,609 // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.610 XcmPassthrough<Origin>,611);612613parameter_types! {614 // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.615 pub UnitWeightCost: Weight = 1_000_000;616 // 1200 UNIQUEs buy 1 second of weight.617 pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), 1_200 * UNIQUE);618 pub const MaxInstructions: u32 = 100;619 pub const MaxAuthorities: u32 = 100_000;620}621622match_type! {623 pub type ParentOrParentsUnitPlurality: impl Contains<MultiLocation> = {624 MultiLocation { parents: 1, interior: Here } |625 MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) }626 };627}628629pub type Barrier = (630 TakeWeightCredit,631 AllowTopLevelPaidExecutionFrom<Everything>,632 AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,633 // ^^^ Parent & its unit plurality gets free execution634);635636pub struct XcmConfig;637impl Config for XcmConfig {638 type Call = Call;639 type XcmSender = XcmRouter;640 // How to withdraw and deposit an asset.641 type AssetTransactor = LocalAssetTransactor;642 type OriginConverter = XcmOriginToTransactDispatchOrigin;643 type IsReserve = NativeAsset;644 type IsTeleporter = (); // Teleportation is disabled645 type LocationInverter = LocationInverter<Ancestry>;646 type Barrier = Barrier;647 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;648 type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;649 type ResponseHandler = (); // Don't handle responses for now.650 type SubscriptionService = PolkadotXcm;651652 type AssetTrap = PolkadotXcm;653 type AssetClaims = PolkadotXcm;654}655656// parameter_types! {657// pub const MaxDownwardMessageWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;658// }659660/// No local origins on this chain are allowed to dispatch XCM sends/executions.661pub type LocalOriginToLocation = (SignedToAccountId32<Origin, AccountId, RelayNetwork>,);662663/// The means for routing XCM messages which are not for local execution into the right message664/// queues.665pub type XcmRouter = (666 // Two routers - use UMP to communicate with the relay chain:667 cumulus_primitives_utility::ParentAsUmp<ParachainSystem, ()>,668 // ..and XCMP to communicate with the sibling chains.669 XcmpQueue,670);671672impl pallet_evm_coder_substrate::Config for Runtime {673 type EthereumTransactionSender = pallet_ethereum::Pallet<Self>;674}675676impl pallet_xcm::Config for Runtime {677 type Event = Event;678 type SendXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;679 type XcmRouter = XcmRouter;680 type ExecuteXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;681 type XcmExecuteFilter = Everything;682 type XcmExecutor = XcmExecutor<XcmConfig>;683 type XcmTeleportFilter = Everything;684 type XcmReserveTransferFilter = Everything;685 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;686 type LocationInverter = LocationInverter<Ancestry>;687 type Origin = Origin;688 type Call = Call;689 const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;690 type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;691}692693impl cumulus_pallet_xcm::Config for Runtime {694 type Event = Event;695 type XcmExecutor = XcmExecutor<XcmConfig>;696}697698impl cumulus_pallet_xcmp_queue::Config for Runtime {699 type Event = Event;700 type XcmExecutor = XcmExecutor<XcmConfig>;701 type ChannelInfo = ParachainSystem;702 type VersionWrapper = ();703}704705impl cumulus_pallet_dmp_queue::Config for Runtime {706 type Event = Event;707 type XcmExecutor = XcmExecutor<XcmConfig>;708 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;709}710711impl pallet_aura::Config for Runtime {712 type AuthorityId = AuraId;713 type DisabledValidators = ();714 type MaxAuthorities = MaxAuthorities;715}716717parameter_types! {718 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account();719 pub const CollectionCreationPrice: Balance = 100 * UNIQUE;720}721722impl pallet_common::Config for Runtime {723 type Event = Event;724 type EvmBackwardsAddressMapping = pallet_common::account::MapBackwardsAddressTruncated;725 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;726 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;727728 type Currency = Balances;729 type CollectionCreationPrice = CollectionCreationPrice;730 type TreasuryAccountId = TreasuryAccountId;731}732733impl pallet_fungible::Config for Runtime {734 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;735}736impl pallet_refungible::Config for Runtime {737 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;738}739impl pallet_nonfungible::Config for Runtime {740 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;741}742743/// Used for the pallet nft in `./nft.rs`744impl pallet_nft::Config for Runtime {745 type WeightInfo = pallet_nft::weights::SubstrateWeight<Self>;746}747748parameter_types! {749 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied750}751752/// Used for the pallet inflation753impl pallet_inflation::Config for Runtime {754 type Currency = Balances;755 type TreasuryAccountId = TreasuryAccountId;756 type InflationBlockInterval = InflationBlockInterval;757}758759parameter_types! {760 pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *761 RuntimeBlockWeights::get().max_block;762 pub const MaxScheduledPerBlock: u32 = 50;763}764765pub struct Sponsoring;766impl SponsoringResolve<AccountId, Call> for Sponsoring {767 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>768 where769 Call: Dispatchable<Info = DispatchInfo>,770 AccountId: AsRef<[u8]>,771 {772 pallet_nft_transaction_payment::Module::<Runtime>::withdraw_type(who, call)773 }774}775776type SponsorshipHandler = (777 pallet_nft::NftSponsorshipHandler<Runtime>,778 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,779);780781impl pallet_unq_scheduler::Config for Runtime {782 type Event = Event;783 type Origin = Origin;784 type PalletsOrigin = OriginCaller;785 type Call = Call;786 type MaximumWeight = MaximumSchedulerWeight;787 type ScheduleOrigin = EnsureSigned<AccountId>;788 type MaxScheduledPerBlock = MaxScheduledPerBlock;789 type SponsorshipHandler = SponsorshipHandler;790 type WeightInfo = ();791}792793impl pallet_nft_transaction_payment::Config for Runtime {794 type SponsorshipHandler = SponsorshipHandler;795}796797impl pallet_evm_transaction_payment::Config for Runtime {798 type SponsorshipHandler = (799 pallet_nft::NftEthSponsorshipHandler<Self>,800 pallet_evm_contract_helpers::HelpersContractSponsoring<Self>,801 );802 type Currency = Balances;803}804805impl pallet_nft_charge_transaction::Config for Runtime {806 type SponsorshipHandler = pallet_nft::NftSponsorshipHandler<Runtime>;807}808809// impl pallet_contract_helpers::Config for Runtime {810// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;811// }812813parameter_types! {814 // 0x842899ECF380553E8a4de75bF534cdf6fBF64049815 pub const HelpersContractAddress: H160 = H160([816 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49,817 ]);818}819820impl pallet_evm_contract_helpers::Config for Runtime {821 type ContractAddress = HelpersContractAddress;822 type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;823}824825construct_runtime!(826 pub enum Runtime where827 Block = Block,828 NodeBlock = opaque::Block,829 UncheckedExtrinsic = UncheckedExtrinsic830 {831 ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event<T>, ValidateUnsigned} = 20,832 ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21,833834 Aura: pallet_aura::{Pallet, Config<T>} = 22,835 AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 23,836837 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 30,838 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 31,839 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 32,840 TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 33,841 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,842 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,843 System: system::{Pallet, Call, Storage, Config, Event<T>} = 36,844 Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,845 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,846847 // XCM helpers.848 XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 50,849 PolkadotXcm: pallet_xcm::{Pallet, Call, Event<T>, Origin} = 51,850 CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event<T>, Origin} = 52,851 DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 53,852853 // Unique Pallets854 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,855 Nft: pallet_nft::{Pallet, Call, Storage} = 61,856 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,857 NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage} = 63,858 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,859 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,860 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,861 Fungible: pallet_fungible::{Pallet, Storage} = 67,862 Refungible: pallet_refungible::{Pallet, Storage} = 68,863 Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,864865 // Frontier866 EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,867 Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,868869 EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,870 EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,871 EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,872 EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,873 }874);875876pub struct TransactionConverter;877878impl fp_rpc::ConvertTransaction<UncheckedExtrinsic> for TransactionConverter {879 fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic {880 UncheckedExtrinsic::new_unsigned(881 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),882 )883 }884}885886impl fp_rpc::ConvertTransaction<opaque::UncheckedExtrinsic> for TransactionConverter {887 fn convert_transaction(888 &self,889 transaction: pallet_ethereum::Transaction,890 ) -> opaque::UncheckedExtrinsic {891 let extrinsic = UncheckedExtrinsic::new_unsigned(892 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),893 );894 let encoded = extrinsic.encode();895 opaque::UncheckedExtrinsic::decode(&mut &encoded[..])896 .expect("Encoded extrinsic is always valid")897 }898}899900/// The address format for describing accounts.901pub type Address = sp_runtime::MultiAddress<AccountId, ()>;902/// Block header type as expected by this runtime.903pub type Header = generic::Header<BlockNumber, BlakeTwo256>;904/// Block type as expected by this runtime.905pub type Block = generic::Block<Header, UncheckedExtrinsic>;906/// A Block signed with a Justification907pub type SignedBlock = generic::SignedBlock<Block>;908/// BlockId type as expected by this runtime.909pub type BlockId = generic::BlockId<Block>;910/// The SignedExtension to the basic transaction logic.911pub type SignedExtra = (912 system::CheckSpecVersion<Runtime>,913 // system::CheckTxVersion<Runtime>,914 system::CheckGenesis<Runtime>,915 system::CheckEra<Runtime>,916 system::CheckNonce<Runtime>,917 system::CheckWeight<Runtime>,918 pallet_nft_charge_transaction::ChargeTransactionPayment<Runtime>,919 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,920);921/// Unchecked extrinsic type as expected by this runtime.922pub type UncheckedExtrinsic =923 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;924/// Extrinsic type that has already been checked.925pub type CheckedExtrinsic = fp_self_contained::CheckedExtrinsic<AccountId, Call, SignedExtra, H160>;926/// Executive: handles dispatch to the various modules.927pub type Executive = frame_executive::Executive<928 Runtime,929 Block,930 frame_system::ChainContext<Runtime>,931 Runtime,932 AllPallets,933>;934935impl_opaque_keys! {936 pub struct SessionKeys {937 pub aura: Aura,938 }939}940941impl fp_self_contained::SelfContainedCall for Call {942 type SignedInfo = H160;943944 fn is_self_contained(&self) -> bool {945 match self {946 Call::Ethereum(call) => call.is_self_contained(),947 _ => false,948 }949 }950951 fn check_self_contained(&self) -> Option<Result<Self::SignedInfo, TransactionValidityError>> {952 match self {953 Call::Ethereum(call) => call.check_self_contained(),954 _ => None,955 }956 }957958 fn validate_self_contained(&self, info: &Self::SignedInfo) -> Option<TransactionValidity> {959 match self {960 Call::Ethereum(call) => call.validate_self_contained(info),961 _ => None,962 }963 }964965 fn pre_dispatch_self_contained(966 &self,967 info: &Self::SignedInfo,968 ) -> Option<Result<(), TransactionValidityError>> {969 match self {970 Call::Ethereum(call) => call.pre_dispatch_self_contained(info),971 _ => None,972 }973 }974975 fn apply_self_contained(976 self,977 info: Self::SignedInfo,978 ) -> Option<sp_runtime::DispatchResultWithInfo<PostDispatchInfoOf<Self>>> {979 match self {980 call @ Call::Ethereum(pallet_ethereum::Call::transact { .. }) => Some(call.dispatch(981 Origin::from(pallet_ethereum::RawOrigin::EthereumTransaction(info)),982 )),983 _ => None,984 }985 }986}987988macro_rules! dispatch_nft_runtime {989 ($collection:ident.$method:ident($($name:ident),*)) => {{990 use pallet_nft::dispatch::Dispatched;991992 let collection = Dispatched::dispatch(<pallet_common::CollectionHandle<Runtime>>::new($collection).unwrap());993 let dispatch = collection.as_dyn();994995 dispatch.$method($($name),*)996 }};997}998impl_runtime_apis! {999 impl up_rpc::NftApi<Block, CrossAccountId, AccountId>1000 for Runtime1001 {1002 fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId> {1003 dispatch_nft_runtime!(collection.account_tokens(account))1004 }1005 fn token_exists(collection: CollectionId, token: TokenId) -> bool {1006 dispatch_nft_runtime!(collection.token_exists(token))1007 }10081009 fn token_owner(collection: CollectionId, token: TokenId) -> CrossAccountId {1010 dispatch_nft_runtime!(collection.token_owner(token))1011 }1012 fn const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8> {1013 dispatch_nft_runtime!(collection.const_metadata(token))1014 }1015 fn variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8> {1016 dispatch_nft_runtime!(collection.variable_metadata(token))1017 }10181019 fn collection_tokens(collection: CollectionId) -> u32 {1020 dispatch_nft_runtime!(collection.collection_tokens())1021 }1022 fn account_balance(collection: CollectionId, account: CrossAccountId) -> u32 {1023 dispatch_nft_runtime!(collection.account_balance(account))1024 }1025 fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> u128 {1026 dispatch_nft_runtime!(collection.balance(account, token))1027 }1028 fn allowance(1029 collection: CollectionId,1030 sender: CrossAccountId,1031 spender: CrossAccountId,1032 token: TokenId,1033 ) -> u128 {1034 dispatch_nft_runtime!(collection.allowance(sender, spender, token))1035 }10361037 fn eth_contract_code(account: H160) -> Option<Vec<u8>> {1038 <pallet_nft::NftErcSupport<Runtime>>::get_code(&account)1039 .or_else(|| <pallet_evm_migration::OnMethodCall<Runtime>>::get_code(&account))1040 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))1041 }1042 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {1043 <pallet_common::Pallet<Runtime>>::adminlist(collection)1044 }1045 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {1046 <pallet_common::Pallet<Runtime>>::allowlist(collection)1047 }1048 fn allowed(collection: CollectionId, user: CrossAccountId) -> bool {1049 <pallet_common::Pallet<Runtime>>::allowed(collection, user)1050 }1051 fn last_token_id(collection: CollectionId) -> TokenId {1052 dispatch_nft_runtime!(collection.last_token_id())1053 }1054 fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>> {1055 <pallet_common::CollectionById<Runtime>>::get(collection)1056 }1057 fn collection_stats() -> CollectionStats {1058 <pallet_common::Pallet<Runtime>>::collection_stats()1059 }1060 }10611062 impl sp_api::Core<Block> for Runtime {1063 fn version() -> RuntimeVersion {1064 VERSION1065 }10661067 fn execute_block(block: Block) {1068 Executive::execute_block(block)1069 }10701071 fn initialize_block(header: &<Block as BlockT>::Header) {1072 Executive::initialize_block(header)1073 }1074 }10751076 impl sp_api::Metadata<Block> for Runtime {1077 fn metadata() -> OpaqueMetadata {1078 OpaqueMetadata::new(Runtime::metadata().into())1079 }1080 }10811082 impl sp_block_builder::BlockBuilder<Block> for Runtime {1083 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {1084 Executive::apply_extrinsic(extrinsic)1085 }10861087 fn finalize_block() -> <Block as BlockT>::Header {1088 Executive::finalize_block()1089 }10901091 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {1092 data.create_extrinsics()1093 }10941095 fn check_inherents(1096 block: Block,1097 data: sp_inherents::InherentData,1098 ) -> sp_inherents::CheckInherentsResult {1099 data.check_extrinsics(&block)1100 }11011102 // fn random_seed() -> <Block as BlockT>::Hash {1103 // RandomnessCollectiveFlip::random_seed().01104 // }1105 }11061107 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {1108 fn validate_transaction(1109 source: TransactionSource,1110 tx: <Block as BlockT>::Extrinsic,1111 hash: <Block as BlockT>::Hash,1112 ) -> TransactionValidity {1113 Executive::validate_transaction(source, tx, hash)1114 }1115 }11161117 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {1118 fn offchain_worker(header: &<Block as BlockT>::Header) {1119 Executive::offchain_worker(header)1120 }1121 }11221123 impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {1124 fn chain_id() -> u64 {1125 <Runtime as pallet_evm::Config>::ChainId::get()1126 }11271128 fn account_basic(address: H160) -> EVMAccount {1129 EVM::account_basic(&address)1130 }11311132 fn gas_price() -> U256 {1133 <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price()1134 }11351136 fn account_code_at(address: H160) -> Vec<u8> {1137 EVM::account_codes(address)1138 }11391140 fn author() -> H160 {1141 <pallet_evm::Pallet<Runtime>>::find_author()1142 }11431144 fn storage_at(address: H160, index: U256) -> H256 {1145 let mut tmp = [0u8; 32];1146 index.to_big_endian(&mut tmp);1147 EVM::account_storages(address, H256::from_slice(&tmp[..]))1148 }11491150 fn call(1151 from: H160,1152 to: H160,1153 data: Vec<u8>,1154 value: U256,1155 gas_limit: U256,1156 gas_price: Option<U256>,1157 nonce: Option<U256>,1158 estimate: bool,1159 ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {1160 let config = if estimate {1161 let mut config = <Runtime as pallet_evm::Config>::config().clone();1162 config.estimate = true;1163 Some(config)1164 } else {1165 None1166 };11671168 <Runtime as pallet_evm::Config>::Runner::call(1169 from,1170 to,1171 data,1172 value,1173 gas_limit.low_u64(),1174 gas_price,1175 nonce,1176 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),1177 ).map_err(|err| err.into())1178 }11791180 fn create(1181 from: H160,1182 data: Vec<u8>,1183 value: U256,1184 gas_limit: U256,1185 gas_price: Option<U256>,1186 nonce: Option<U256>,1187 estimate: bool,1188 ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {1189 let config = if estimate {1190 let mut config = <Runtime as pallet_evm::Config>::config().clone();1191 config.estimate = true;1192 Some(config)1193 } else {1194 None1195 };11961197 <Runtime as pallet_evm::Config>::Runner::create(1198 from,1199 data,1200 value,1201 gas_limit.low_u64(),1202 gas_price,1203 nonce,1204 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),1205 ).map_err(|err| err.into())1206 }12071208 fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {1209 Ethereum::current_transaction_statuses()1210 }12111212 fn current_block() -> Option<pallet_ethereum::Block> {1213 Ethereum::current_block()1214 }12151216 fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {1217 Ethereum::current_receipts()1218 }12191220 fn current_all() -> (1221 Option<pallet_ethereum::Block>,1222 Option<Vec<pallet_ethereum::Receipt>>,1223 Option<Vec<TransactionStatus>>1224 ) {1225 (1226 Ethereum::current_block(),1227 Ethereum::current_receipts(),1228 Ethereum::current_transaction_statuses()1229 )1230 }12311232 fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {1233 xts.into_iter().filter_map(|xt| match xt.0.function {1234 Call::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),1235 _ => None1236 }).collect()1237 }1238 }12391240 impl sp_session::SessionKeys<Block> for Runtime {1241 fn decode_session_keys(1242 encoded: Vec<u8>,1243 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {1244 SessionKeys::decode_into_raw_public_keys(&encoded)1245 }12461247 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {1248 SessionKeys::generate(seed)1249 }1250 }12511252 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {1253 fn slot_duration() -> sp_consensus_aura::SlotDuration {1254 sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())1255 }12561257 fn authorities() -> Vec<AuraId> {1258 Aura::authorities().to_vec()1259 }1260 }12611262 impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {1263 fn collect_collation_info() -> cumulus_primitives_core::CollationInfo {1264 ParachainSystem::collect_collation_info()1265 }1266 }12671268 impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {1269 fn account_nonce(account: AccountId) -> Index {1270 System::account_nonce(account)1271 }1272 }12731274 impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {1275 fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {1276 TransactionPayment::query_info(uxt, len)1277 }1278 fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {1279 TransactionPayment::query_fee_details(uxt, len)1280 }1281 }12821283 /*1284 impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>1285 for Runtime1286 {1287 fn call(1288 origin: AccountId,1289 dest: AccountId,1290 value: Balance,1291 gas_limit: u64,1292 input_data: Vec<u8>,1293 ) -> pallet_contracts_primitives::ContractExecResult {1294 Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)1295 }12961297 fn instantiate(1298 origin: AccountId,1299 endowment: Balance,1300 gas_limit: u64,1301 code: pallet_contracts_primitives::Code<Hash>,1302 data: Vec<u8>,1303 salt: Vec<u8>,1304 ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>1305 {1306 Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)1307 }13081309 fn get_storage(1310 address: AccountId,1311 key: [u8; 32],1312 ) -> pallet_contracts_primitives::GetStorageResult {1313 Contracts::get_storage(address, key)1314 }13151316 fn rent_projection(1317 address: AccountId,1318 ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {1319 Contracts::rent_projection(address)1320 }1321 }1322 */13231324 #[cfg(feature = "runtime-benchmarks")]1325 impl frame_benchmarking::Benchmark<Block> for Runtime {1326 fn benchmark_metadata(extra: bool) -> (1327 Vec<frame_benchmarking::BenchmarkList>,1328 Vec<frame_support::traits::StorageInfo>,1329 ) {1330 use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};1331 use frame_support::traits::StorageInfoTrait;13321333 let mut list = Vec::<BenchmarkList>::new();13341335 list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);1336 list_benchmark!(list, extra, pallet_nft, Nft);1337 list_benchmark!(list, extra, pallet_inflation, Inflation);1338 list_benchmark!(list, extra, pallet_fungible, Fungible);1339 list_benchmark!(list, extra, pallet_refungible, Refungible);1340 list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);13411342 let storage_info = AllPalletsWithSystem::storage_info();13431344 return (list, storage_info)1345 }13461347 fn dispatch_benchmark(1348 config: frame_benchmarking::BenchmarkConfig1349 ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {1350 use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};13511352 let allowlist: Vec<TrackedStorageKey> = vec![1353 // Block Number1354 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),1355 // Total Issuance1356 hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),1357 // Execution Phase1358 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),1359 // Event Count1360 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),1361 // System Events1362 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),1363 ];13641365 let mut batches = Vec::<BenchmarkBatch>::new();1366 let params = (&config, &allowlist);13671368 add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);1369 add_benchmark!(params, batches, pallet_nft, Nft);1370 add_benchmark!(params, batches, pallet_inflation, Inflation);1371 add_benchmark!(params, batches, pallet_fungible, Fungible);1372 add_benchmark!(params, batches, pallet_refungible, Refungible);1373 add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);13741375 if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }1376 Ok(batches)1377 }1378 }1379}13801381struct CheckInherents;13821383impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {1384 fn check_inherents(1385 block: &Block,1386 relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof,1387 ) -> sp_inherents::CheckInherentsResult {1388 let relay_chain_slot = relay_state_proof1389 .read_slot()1390 .expect("Could not read the relay chain slot from the proof");13911392 let inherent_data =1393 cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(1394 relay_chain_slot,1395 sp_std::time::Duration::from_secs(6),1396 )1397 .create_inherent_data()1398 .expect("Could not create the timestamp inherent data");13991400 inherent_data.check_extrinsics(block)1401 }1402}14031404cumulus_pallet_parachain_system::register_validate_block!(1405 Runtime = Runtime,1406 BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,1407 CheckInherents = CheckInherents,1408);1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.78#![cfg_attr(not(feature = "std"), no_std)]9// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.10#![recursion_limit = "1024"]11#![allow(clippy::from_over_into, clippy::identity_op)]12#![allow(clippy::fn_to_numeric_cast_with_truncation)]13// Make the WASM binary available.14#[cfg(feature = "std")]15include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));1617use sp_api::impl_runtime_apis;18use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H256, U256, H160};19// #[cfg(any(feature = "std", test))]20// pub use sp_runtime::BuildStorage;2122use sp_runtime::{23 Permill, Perbill, Percent, create_runtime_str, generic, impl_opaque_keys,24 traits::{25 AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify, AccountIdConversion,26 },27 transaction_validity::{TransactionSource, TransactionValidity},28 ApplyExtrinsicResult, MultiSignature,29};3031use sp_std::prelude::*;3233#[cfg(feature = "std")]34use sp_version::NativeVersion;35use sp_version::RuntimeVersion;36pub use pallet_transaction_payment::{37 Multiplier, TargetedFeeAdjustment, FeeDetails, RuntimeDispatchInfo,38};39// A few exports that help ease life for downstream crates.40pub use pallet_balances::Call as BalancesCall;41pub use pallet_evm::{EnsureAddressTruncated, HashedAddressMapping, Runner};42pub use frame_support::{43 construct_runtime, match_type,44 dispatch::DispatchResult,45 PalletId, parameter_types, StorageValue, ConsensusEngineId,46 traits::{47 Everything, Currency, ExistenceRequirement, Get, IsInVec, KeyOwnerProofSystem,48 LockIdentifier, OnUnbalanced, Randomness, FindAuthor,49 },50 weights::{51 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},52 DispatchClass, DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,53 WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients,54 },55};56use nft_data_structs::*;57// use pallet_contracts::weights::WeightInfo;58// #[cfg(any(feature = "std", test))]59use frame_system::{60 self as system, EnsureRoot, EnsureSigned,61 limits::{BlockWeights, BlockLength},62};63use sp_arithmetic::{64 traits::{BaseArithmetic, Unsigned},65};66use smallvec::smallvec;67use codec::{Encode, Decode};68use pallet_evm::{Account as EVMAccount, FeeCalculator, OnMethodCall};69use fp_rpc::TransactionStatus;70use sp_core::crypto::Public;71use sp_runtime::{72 traits::{BlockNumberProvider, Dispatchable, PostDispatchInfoOf},73 transaction_validity::TransactionValidityError,74};7576// pub use pallet_timestamp::Call as TimestampCall;77pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;7879// Polkadot imports80use pallet_xcm::XcmPassthrough;81use polkadot_parachain::primitives::Sibling;82use xcm::v1::{BodyId, Junction::*, MultiLocation, NetworkId, Junctions::*};83use xcm_builder::{84 AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter,85 EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, NativeAsset,86 ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative,87 SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,88 SovereignSignedViaLocation, TakeWeightCredit, UsingComponents,89};90use xcm_executor::{Config, XcmExecutor};9192// mod chain_extension;93// use crate::chain_extension::{NFTExtension, Imbalance};9495/// An index to a block.96pub type BlockNumber = u32;9798/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.99pub type Signature = MultiSignature;100101/// Some way of identifying an account on the chain. We intentionally make it equivalent102/// to the public key of our transaction signing scheme.103pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;104105pub type CrossAccountId = pallet_common::account::BasicCrossAccountId<Runtime>;106107/// The type for looking up accounts. We don't expect more than 4 billion of them, but you108/// never know...109pub type AccountIndex = u32;110111/// Balance of an account.112pub type Balance = u128;113114/// Index of a transaction in the chain.115pub type Index = u32;116117/// A hash of some data used by the chain.118pub type Hash = sp_core::H256;119120/// Digest item type.121pub type DigestItem = generic::DigestItem<Hash>;122123/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know124/// the specifics of the runtime. They can then be made to be agnostic over specific formats125/// of data like extrinsics, allowing for them to continue syncing the network through upgrades126/// to even the core data structures.127pub mod opaque {128 use super::*;129130 pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;131132 /// Opaque block type.133 pub type Block = generic::Block<Header, UncheckedExtrinsic>;134135 pub type SessionHandlers = ();136137 impl_opaque_keys! {138 pub struct SessionKeys {139 pub aura: Aura,140 }141 }142}143144/// This runtime version.145pub const VERSION: RuntimeVersion = RuntimeVersion {146 spec_name: create_runtime_str!("opal"),147 impl_name: create_runtime_str!("opal"),148 authoring_version: 1,149 spec_version: 912202,150 impl_version: 1,151 apis: RUNTIME_API_VERSIONS,152 transaction_version: 1,153};154155pub const MILLISECS_PER_BLOCK: u64 = 12000;156157pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;158159// These time units are defined in number of blocks.160pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);161pub const HOURS: BlockNumber = MINUTES * 60;162pub const DAYS: BlockNumber = HOURS * 24;163164parameter_types! {165 pub const DefaultSponsoringRateLimit: BlockNumber = 1 * DAYS;166}167168#[derive(codec::Encode, codec::Decode)]169pub enum XCMPMessage<XAccountId, XBalance> {170 /// Transfer tokens to the given account from the Parachain account.171 TransferToken(XAccountId, XBalance),172}173174/// The version information used to identify this runtime when compiled natively.175#[cfg(feature = "std")]176pub fn native_version() -> NativeVersion {177 NativeVersion {178 runtime_version: VERSION,179 can_author_with: Default::default(),180 }181}182183type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;184185pub struct DealWithFees;186impl OnUnbalanced<NegativeImbalance> for DealWithFees {187 fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) {188 if let Some(fees) = fees_then_tips.next() {189 // for fees, 100% to treasury190 let mut split = fees.ration(100, 0);191 if let Some(tips) = fees_then_tips.next() {192 // for tips, if any, 100% to treasury193 tips.ration_merge_into(100, 0, &mut split);194 }195 Treasury::on_unbalanced(split.0);196 // Author::on_unbalanced(split.1);197 }198 }199}200201/// We assume that ~10% of the block weight is consumed by `on_initalize` handlers.202/// This is used to limit the maximal weight of a single extrinsic.203const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);204/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used205/// by Operational extrinsics.206const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);207/// We allow for 2 seconds of compute with a 6 second average block time.208const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2;209210parameter_types! {211 pub const BlockHashCount: BlockNumber = 2400;212 pub RuntimeBlockLength: BlockLength =213 BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);214 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);215 pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;216 pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()217 .base_block(BlockExecutionWeight::get())218 .for_class(DispatchClass::all(), |weights| {219 weights.base_extrinsic = ExtrinsicBaseWeight::get();220 })221 .for_class(DispatchClass::Normal, |weights| {222 weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);223 })224 .for_class(DispatchClass::Operational, |weights| {225 weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);226 // Operational transactions have some extra reserved space, so that they227 // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.228 weights.reserved = Some(229 MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT230 );231 })232 .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)233 .build_or_panic();234 pub const Version: RuntimeVersion = VERSION;235 pub const SS58Prefix: u8 = 42;236}237238parameter_types! {239 pub const ChainId: u64 = 8888;240}241242pub struct FixedFee;243impl FeeCalculator for FixedFee {244 fn min_gas_price() -> U256 {245 1.into()246 }247}248249impl pallet_evm::Config for Runtime {250 type BlockGasLimit = BlockGasLimit;251 type FeeCalculator = FixedFee;252 type GasWeightMapping = ();253 type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;254 type CallOrigin = EnsureAddressTruncated;255 type WithdrawOrigin = EnsureAddressTruncated;256 type AddressMapping = HashedAddressMapping<Self::Hashing>;257 type Precompiles = ();258 type Currency = Balances;259 type Event = Event;260 type OnMethodCall = (261 pallet_evm_migration::OnMethodCall<Self>,262 pallet_nft::NftErcSupport<Self>,263 pallet_evm_contract_helpers::HelpersOnMethodCall<Self>,264 );265 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;266 type ChainId = ChainId;267 type Runner = pallet_evm::runner::stack::Runner<Self>;268 type OnChargeTransaction = pallet_evm_transaction_payment::OnChargeTransaction<Self>;269 type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack<Self>;270 type FindAuthor = EthereumFindAuthor<Aura>;271}272273impl pallet_evm_migration::Config for Runtime {274 type WeightInfo = pallet_evm_migration::weights::SubstrateWeight<Self>;275}276277pub struct EthereumFindAuthor<F>(core::marker::PhantomData<F>);278impl<F: FindAuthor<u32>> FindAuthor<H160> for EthereumFindAuthor<F> {279 fn find_author<'a, I>(digests: I) -> Option<H160>280 where281 I: 'a + IntoIterator<Item = (ConsensusEngineId, &'a [u8])>,282 {283 if let Some(author_index) = F::find_author(digests) {284 let authority_id = Aura::authorities()[author_index as usize].clone();285 return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24]));286 }287 None288 }289}290291parameter_types! {292 pub BlockGasLimit: U256 = U256::from(u32::max_value());293}294295impl pallet_ethereum::Config for Runtime {296 type Event = Event;297 type StateRoot = pallet_ethereum::IntermediateStateRoot;298 type EvmSubmitLog = pallet_evm::Pallet<Self>;299}300301impl pallet_randomness_collective_flip::Config for Runtime {}302303impl system::Config for Runtime {304 /// The data to be stored in an account.305 type AccountData = pallet_balances::AccountData<Balance>;306 /// The identifier used to distinguish between accounts.307 type AccountId = AccountId;308 /// The basic call filter to use in dispatchable.309 type BaseCallFilter = Everything;310 /// Maximum number of block number to block hash mappings to keep (oldest pruned first).311 type BlockHashCount = BlockHashCount;312 /// The maximum length of a block (in bytes).313 type BlockLength = RuntimeBlockLength;314 /// The index type for blocks.315 type BlockNumber = BlockNumber;316 /// The weight of the overhead invoked on the block import process, independent of the extrinsics included in that block.317 type BlockWeights = RuntimeBlockWeights;318 /// The aggregated dispatch type that is available for extrinsics.319 type Call = Call;320 /// The weight of database operations that the runtime can invoke.321 type DbWeight = RocksDbWeight;322 /// The ubiquitous event type.323 type Event = Event;324 /// The type for hashing blocks and tries.325 type Hash = Hash;326 /// The hashing algorithm used.327 type Hashing = BlakeTwo256;328 /// The header type.329 type Header = generic::Header<BlockNumber, BlakeTwo256>;330 /// The index type for storing how many extrinsics an account has signed.331 type Index = Index;332 /// The lookup mechanism to get account ID from whatever is passed in dispatchers.333 type Lookup = AccountIdLookup<AccountId, ()>;334 /// What to do if an account is fully reaped from the system.335 type OnKilledAccount = ();336 /// What to do if a new account is created.337 type OnNewAccount = ();338 type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;339 /// The ubiquitous origin type.340 type Origin = Origin;341 /// This type is being generated by `construct_runtime!`.342 type PalletInfo = PalletInfo;343 /// This is used as an identifier of the chain. 42 is the generic substrate prefix.344 type SS58Prefix = SS58Prefix;345 /// Weight information for the extrinsics of this pallet.346 type SystemWeightInfo = system::weights::SubstrateWeight<Self>;347 /// Version of the runtime.348 type Version = Version;349}350351parameter_types! {352 pub const MinimumPeriod: u64 = SLOT_DURATION / 2;353}354355impl pallet_timestamp::Config for Runtime {356 /// A timestamp: milliseconds since the unix epoch.357 type Moment = u64;358 type OnTimestampSet = ();359 type MinimumPeriod = MinimumPeriod;360 type WeightInfo = ();361}362363parameter_types! {364 // pub const ExistentialDeposit: u128 = 500;365 pub const ExistentialDeposit: u128 = 0;366 pub const MaxLocks: u32 = 50;367}368369impl pallet_balances::Config for Runtime {370 type MaxLocks = MaxLocks;371 type MaxReserves = ();372 type ReserveIdentifier = [u8; 8];373 /// The type for recording an account's balance.374 type Balance = Balance;375 /// The ubiquitous event type.376 type Event = Event;377 type DustRemoval = Treasury;378 type ExistentialDeposit = ExistentialDeposit;379 type AccountStore = System;380 type WeightInfo = pallet_balances::weights::SubstrateWeight<Self>;381}382383pub const MICROUNIQUE: Balance = 1_000_000_000;384pub const MILLIUNIQUE: Balance = 1_000 * MICROUNIQUE;385pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE;386pub const UNIQUE: Balance = 100 * CENTIUNIQUE;387388pub const fn deposit(items: u32, bytes: u32) -> Balance {389 items as Balance * 15 * CENTIUNIQUE + (bytes as Balance) * 6 * CENTIUNIQUE390}391392/*393parameter_types! {394 pub TombstoneDeposit: Balance = deposit(395 1,396 sp_std::mem::size_of::<pallet_contracts::Pallet<Runtime>> as u32,397 );398 pub DepositPerContract: Balance = TombstoneDeposit::get();399 pub const DepositPerStorageByte: Balance = deposit(0, 1);400 pub const DepositPerStorageItem: Balance = deposit(1, 0);401 pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);402 pub const SurchargeReward: Balance = 150 * MILLIUNIQUE;403 pub const SignedClaimHandicap: u32 = 2;404 pub const MaxDepth: u32 = 32;405 pub const MaxValueSize: u32 = 16 * 1024;406 pub const MaxCodeSize: u32 = 1024 * 1024 * 25; // 25 Mb407 // The lazy deletion runs inside on_initialize.408 pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO *409 RuntimeBlockWeights::get().max_block;410 // The weight needed for decoding the queue should be less or equal than a fifth411 // of the overall weight dedicated to the lazy deletion.412 pub DeletionQueueDepth: u32 = ((DeletionWeightLimit::get() / (413 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(1) -414 <Runtime as pallet_contracts::Config>::WeightInfo::on_initialize_per_queue_item(0)415 )) / 5) as u32;416 pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();417}418419impl pallet_contracts::Config for Runtime {420 type Time = Timestamp;421 type Randomness = RandomnessCollectiveFlip;422 type Currency = Balances;423 type Event = Event;424 type RentPayment = ();425 type SignedClaimHandicap = SignedClaimHandicap;426 type TombstoneDeposit = TombstoneDeposit;427 type DepositPerContract = DepositPerContract;428 type DepositPerStorageByte = DepositPerStorageByte;429 type DepositPerStorageItem = DepositPerStorageItem;430 type RentFraction = RentFraction;431 type SurchargeReward = SurchargeReward;432 type WeightPrice = pallet_transaction_payment::Pallet<Self>;433 type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;434 type ChainExtension = NFTExtension;435 type DeletionQueueDepth = DeletionQueueDepth;436 type DeletionWeightLimit = DeletionWeightLimit;437 type Schedule = Schedule;438 type CallStack = [pallet_contracts::Frame<Self>; 31];439}440*/441442parameter_types! {443 pub const TransactionByteFee: Balance = 501 * MICROUNIQUE; // Targeting 0.1 Unique per NFT transfer444 /// This value increases the priority of `Operational` transactions by adding445 /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.446 pub const OperationalFeeMultiplier: u8 = 5;447}448449/// Linear implementor of `WeightToFeePolynomial`450pub struct LinearFee<T>(sp_std::marker::PhantomData<T>);451452impl<T> WeightToFeePolynomial for LinearFee<T>453where454 T: BaseArithmetic + From<u32> + Copy + Unsigned,455{456 type Balance = T;457458 fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {459 smallvec!(WeightToFeeCoefficient {460 coeff_integer: 146_700u32.into(), // Targeting 0.1 Unique per NFT transfer461 coeff_frac: Perbill::zero(),462 negative: false,463 degree: 1,464 })465 }466}467468impl pallet_transaction_payment::Config for Runtime {469 type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter<Balances, DealWithFees>;470 type TransactionByteFee = TransactionByteFee;471 type OperationalFeeMultiplier = OperationalFeeMultiplier;472 type WeightToFee = LinearFee<Balance>;473 type FeeMultiplierUpdate = ();474}475476parameter_types! {477 pub const ProposalBond: Permill = Permill::from_percent(5);478 pub const ProposalBondMinimum: Balance = 1 * UNIQUE;479 pub const SpendPeriod: BlockNumber = 5 * MINUTES;480 pub const Burn: Permill = Permill::from_percent(0);481 pub const TipCountdown: BlockNumber = 1 * DAYS;482 pub const TipFindersFee: Percent = Percent::from_percent(20);483 pub const TipReportDepositBase: Balance = 1 * UNIQUE;484 pub const DataDepositPerByte: Balance = 1 * CENTIUNIQUE;485 pub const BountyDepositBase: Balance = 1 * UNIQUE;486 pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS;487 pub const TreasuryModuleId: PalletId = PalletId(*b"py/trsry");488 pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS;489 pub const MaximumReasonLength: u32 = 16384;490 pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);491 pub const BountyValueMinimum: Balance = 5 * UNIQUE;492 pub const MaxApprovals: u32 = 100;493}494495impl pallet_treasury::Config for Runtime {496 type PalletId = TreasuryModuleId;497 type Currency = Balances;498 type ApproveOrigin = EnsureRoot<AccountId>;499 type RejectOrigin = EnsureRoot<AccountId>;500 type Event = Event;501 type OnSlash = ();502 type ProposalBond = ProposalBond;503 type ProposalBondMinimum = ProposalBondMinimum;504 type SpendPeriod = SpendPeriod;505 type Burn = Burn;506 type BurnDestination = ();507 type SpendFunds = ();508 type WeightInfo = pallet_treasury::weights::SubstrateWeight<Self>;509 type MaxApprovals = MaxApprovals;510}511512impl pallet_sudo::Config for Runtime {513 type Event = Event;514 type Call = Call;515}516517pub struct RelayChainBlockNumberProvider<T>(sp_std::marker::PhantomData<T>);518519impl<T: cumulus_pallet_parachain_system::Config> BlockNumberProvider520 for RelayChainBlockNumberProvider<T>521{522 type BlockNumber = BlockNumber;523524 fn current_block_number() -> Self::BlockNumber {525 cumulus_pallet_parachain_system::Pallet::<T>::validation_data()526 .map(|d| d.relay_parent_number)527 .unwrap_or_default()528 }529}530531parameter_types! {532 pub const MinVestedTransfer: Balance = 10 * UNIQUE;533 pub const MaxVestingSchedules: u32 = 28;534}535536impl orml_vesting::Config for Runtime {537 type Event = Event;538 type Currency = pallet_balances::Pallet<Runtime>;539 type MinVestedTransfer = MinVestedTransfer;540 type VestedTransferOrigin = EnsureSigned<AccountId>;541 type WeightInfo = ();542 type MaxVestingSchedules = MaxVestingSchedules;543 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;544}545546parameter_types! {547 pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;548 pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;549}550551impl cumulus_pallet_parachain_system::Config for Runtime {552 type Event = Event;553 type OnValidationData = ();554 type SelfParaId = parachain_info::Pallet<Self>;555 // type DownwardMessageHandlers = cumulus_primitives_utility::UnqueuedDmpAsParent<556 // MaxDownwardMessageWeight,557 // XcmExecutor<XcmConfig>,558 // Call,559 // >;560 type OutboundXcmpMessageSource = XcmpQueue;561 type DmpMessageHandler = DmpQueue;562 type ReservedDmpWeight = ReservedDmpWeight;563 type ReservedXcmpWeight = ReservedXcmpWeight;564 type XcmpMessageHandler = XcmpQueue;565}566567impl parachain_info::Config for Runtime {}568569impl cumulus_pallet_aura_ext::Config for Runtime {}570571parameter_types! {572 pub const RelayLocation: MultiLocation = MultiLocation::parent();573 pub const RelayNetwork: NetworkId = NetworkId::Polkadot;574 pub RelayOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();575 pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();576}577578/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used579/// when determining ownership of accounts for asset transacting and when attempting to use XCM580/// `Transact` in order to determine the dispatch Origin.581pub type LocationToAccountId = (582 // The parent (Relay-chain) origin converts to the default `AccountId`.583 ParentIsDefault<AccountId>,584 // Sibling parachain origins convert to AccountId via the `ParaId::into`.585 SiblingParachainConvertsVia<Sibling, AccountId>,586 // Straight up local `AccountId32` origins just alias directly to `AccountId`.587 AccountId32Aliases<RelayNetwork, AccountId>,588);589590/// Means for transacting assets on this chain.591pub type LocalAssetTransactor = CurrencyAdapter<592 // Use this currency:593 Balances,594 // Use this currency when it is a fungible asset matching the given location or name:595 IsConcrete<RelayLocation>,596 // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:597 LocationToAccountId,598 // Our chain's account ID type (we can't get away without mentioning it explicitly):599 AccountId,600 // We don't track any teleports.601 (),602>;603604/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,605/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can606/// biases the kind of local `Origin` it will become.607pub type XcmOriginToTransactDispatchOrigin = (608 // Sovereign account converter; this attempts to derive an `AccountId` from the origin location609 // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for610 // foreign chains who want to have a local sovereign account on this chain which they control.611 SovereignSignedViaLocation<LocationToAccountId, Origin>,612 // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when613 // recognised.614 RelayChainAsNative<RelayOrigin, Origin>,615 // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when616 // recognised.617 SiblingParachainAsNative<cumulus_pallet_xcm::Origin, Origin>,618 // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a619 // transaction from the Root origin.620 ParentAsSuperuser<Origin>,621 // Native signed account converter; this just converts an `AccountId32` origin into a normal622 // `Origin::Signed` origin of the same 32-byte value.623 SignedAccountId32AsNative<RelayNetwork, Origin>,624 // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.625 XcmPassthrough<Origin>,626);627628parameter_types! {629 // One XCM operation is 1_000_000 weight - almost certainly a conservative estimate.630 pub UnitWeightCost: Weight = 1_000_000;631 // 1200 UNIQUEs buy 1 second of weight.632 pub const WeightPrice: (MultiLocation, u128) = (MultiLocation::parent(), 1_200 * UNIQUE);633 pub const MaxInstructions: u32 = 100;634 pub const MaxAuthorities: u32 = 100_000;635}636637match_type! {638 pub type ParentOrParentsUnitPlurality: impl Contains<MultiLocation> = {639 MultiLocation { parents: 1, interior: Here } |640 MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) }641 };642}643644pub type Barrier = (645 TakeWeightCredit,646 AllowTopLevelPaidExecutionFrom<Everything>,647 AllowUnpaidExecutionFrom<ParentOrParentsUnitPlurality>,648 // ^^^ Parent & its unit plurality gets free execution649);650651pub struct XcmConfig;652impl Config for XcmConfig {653 type Call = Call;654 type XcmSender = XcmRouter;655 // How to withdraw and deposit an asset.656 type AssetTransactor = LocalAssetTransactor;657 type OriginConverter = XcmOriginToTransactDispatchOrigin;658 type IsReserve = NativeAsset;659 type IsTeleporter = (); // Teleportation is disabled660 type LocationInverter = LocationInverter<Ancestry>;661 type Barrier = Barrier;662 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;663 type Trader = UsingComponents<IdentityFee<Balance>, RelayLocation, AccountId, Balances, ()>;664 type ResponseHandler = (); // Don't handle responses for now.665 type SubscriptionService = PolkadotXcm;666667 type AssetTrap = PolkadotXcm;668 type AssetClaims = PolkadotXcm;669}670671// parameter_types! {672// pub const MaxDownwardMessageWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10;673// }674675/// No local origins on this chain are allowed to dispatch XCM sends/executions.676pub type LocalOriginToLocation = (SignedToAccountId32<Origin, AccountId, RelayNetwork>,);677678/// The means for routing XCM messages which are not for local execution into the right message679/// queues.680pub type XcmRouter = (681 // Two routers - use UMP to communicate with the relay chain:682 cumulus_primitives_utility::ParentAsUmp<ParachainSystem, ()>,683 // ..and XCMP to communicate with the sibling chains.684 XcmpQueue,685);686687impl pallet_evm_coder_substrate::Config for Runtime {688 type EthereumTransactionSender = pallet_ethereum::Pallet<Self>;689}690691impl pallet_xcm::Config for Runtime {692 type Event = Event;693 type SendXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;694 type XcmRouter = XcmRouter;695 type ExecuteXcmOrigin = EnsureXcmOrigin<Origin, LocalOriginToLocation>;696 type XcmExecuteFilter = Everything;697 type XcmExecutor = XcmExecutor<XcmConfig>;698 type XcmTeleportFilter = Everything;699 type XcmReserveTransferFilter = Everything;700 type Weigher = FixedWeightBounds<UnitWeightCost, Call, MaxInstructions>;701 type LocationInverter = LocationInverter<Ancestry>;702 type Origin = Origin;703 type Call = Call;704 const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;705 type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;706}707708impl cumulus_pallet_xcm::Config for Runtime {709 type Event = Event;710 type XcmExecutor = XcmExecutor<XcmConfig>;711}712713impl cumulus_pallet_xcmp_queue::Config for Runtime {714 type Event = Event;715 type XcmExecutor = XcmExecutor<XcmConfig>;716 type ChannelInfo = ParachainSystem;717 type VersionWrapper = ();718}719720impl cumulus_pallet_dmp_queue::Config for Runtime {721 type Event = Event;722 type XcmExecutor = XcmExecutor<XcmConfig>;723 type ExecuteOverweightOrigin = frame_system::EnsureRoot<AccountId>;724}725726impl pallet_aura::Config for Runtime {727 type AuthorityId = AuraId;728 type DisabledValidators = ();729 type MaxAuthorities = MaxAuthorities;730}731732parameter_types! {733 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account();734 pub const CollectionCreationPrice: Balance = 100 * UNIQUE;735}736737impl pallet_common::Config for Runtime {738 type Event = Event;739 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;740 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;741 type CrossAccountId = pallet_common::account::BasicCrossAccountId<Self>;742743 type Currency = Balances;744 type CollectionCreationPrice = CollectionCreationPrice;745 type TreasuryAccountId = TreasuryAccountId;746}747748impl pallet_fungible::Config for Runtime {749 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;750}751impl pallet_refungible::Config for Runtime {752 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;753}754impl pallet_nonfungible::Config for Runtime {755 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;756}757758/// Used for the pallet nft in `./nft.rs`759impl pallet_nft::Config for Runtime {760 type WeightInfo = pallet_nft::weights::SubstrateWeight<Self>;761}762763parameter_types! {764 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied765}766767/// Used for the pallet inflation768impl pallet_inflation::Config for Runtime {769 type Currency = Balances;770 type TreasuryAccountId = TreasuryAccountId;771 type InflationBlockInterval = InflationBlockInterval;772}773774parameter_types! {775 pub MaximumSchedulerWeight: Weight = Perbill::from_percent(50) *776 RuntimeBlockWeights::get().max_block;777 pub const MaxScheduledPerBlock: u32 = 50;778}779780type EvmSponsorshipHandler = (781 pallet_nft::NftEthSponsorshipHandler<Runtime>,782 pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,783);784type SponsorshipHandler = (785 pallet_nft::NftSponsorshipHandler<Runtime>,786 //pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,787 pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,788);789790impl pallet_unq_scheduler::Config for Runtime {791 type Event = Event;792 type Origin = Origin;793 type PalletsOrigin = OriginCaller;794 type Call = Call;795 type MaximumWeight = MaximumSchedulerWeight;796 type ScheduleOrigin = EnsureSigned<AccountId>;797 type MaxScheduledPerBlock = MaxScheduledPerBlock;798 type SponsorshipHandler = SponsorshipHandler;799 type WeightInfo = ();800}801802impl pallet_evm_transaction_payment::Config for Runtime {803 type EvmSponsorshipHandler = EvmSponsorshipHandler;804 type Currency = Balances;805 type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;806 type EvmBackwardsAddressMapping = up_evm_mapping::MapBackwardsAddressTruncated;807}808809impl pallet_nft_charge_transaction::Config for Runtime {810 type SponsorshipHandler = SponsorshipHandler;811}812813// impl pallet_contract_helpers::Config for Runtime {814// type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;815// }816817parameter_types! {818 // 0x842899ECF380553E8a4de75bF534cdf6fBF64049819 pub const HelpersContractAddress: H160 = H160([820 0x84, 0x28, 0x99, 0xec, 0xf3, 0x80, 0x55, 0x3e, 0x8a, 0x4d, 0xe7, 0x5b, 0xf5, 0x34, 0xcd, 0xf6, 0xfb, 0xf6, 0x40, 0x49,821 ]);822}823824impl pallet_evm_contract_helpers::Config for Runtime {825 type ContractAddress = HelpersContractAddress;826 type DefaultSponsoringRateLimit = DefaultSponsoringRateLimit;827}828829construct_runtime!(830 pub enum Runtime where831 Block = Block,832 NodeBlock = opaque::Block,833 UncheckedExtrinsic = UncheckedExtrinsic834 {835 ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event<T>, ValidateUnsigned} = 20,836 ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21,837838 Aura: pallet_aura::{Pallet, Config<T>} = 22,839 AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 23,840841 Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 30,842 RandomnessCollectiveFlip: pallet_randomness_collective_flip::{Pallet, Storage} = 31,843 Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 32,844 TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 33,845 Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>} = 34,846 Sudo: pallet_sudo::{Pallet, Call, Storage, Config<T>, Event<T>} = 35,847 System: system::{Pallet, Call, Storage, Config, Event<T>} = 36,848 Vesting: orml_vesting::{Pallet, Storage, Call, Event<T>, Config<T>} = 37,849 // Vesting: pallet_vesting::{Pallet, Call, Config<T>, Storage, Event<T>} = 37,850 // Contracts: pallet_contracts::{Pallet, Call, Storage, Event<T>} = 38,851852 // XCM helpers.853 XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 50,854 PolkadotXcm: pallet_xcm::{Pallet, Call, Event<T>, Origin} = 51,855 CumulusXcm: cumulus_pallet_xcm::{Pallet, Call, Event<T>, Origin} = 52,856 DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 53,857858 // Unique Pallets859 Inflation: pallet_inflation::{Pallet, Call, Storage} = 60,860 Nft: pallet_nft::{Pallet, Call, Storage} = 61,861 Scheduler: pallet_unq_scheduler::{Pallet, Call, Storage, Event<T>} = 62,862 // free = 63863 Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage } = 64,864 // ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage} = 65,865 Common: pallet_common::{Pallet, Storage, Event<T>} = 66,866 Fungible: pallet_fungible::{Pallet, Storage} = 67,867 Refungible: pallet_refungible::{Pallet, Storage} = 68,868 Nonfungible: pallet_nonfungible::{Pallet, Storage} = 69,869870 // Frontier871 EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 100,872 Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 101,873874 EvmCoderSubstrate: pallet_evm_coder_substrate::{Pallet, Storage} = 150,875 EvmContractHelpers: pallet_evm_contract_helpers::{Pallet, Storage} = 151,876 EvmTransactionPayment: pallet_evm_transaction_payment::{Pallet} = 152,877 EvmMigration: pallet_evm_migration::{Pallet, Call, Storage} = 153,878 }879);880881pub struct TransactionConverter;882883impl fp_rpc::ConvertTransaction<UncheckedExtrinsic> for TransactionConverter {884 fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic {885 UncheckedExtrinsic::new_unsigned(886 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),887 )888 }889}890891impl fp_rpc::ConvertTransaction<opaque::UncheckedExtrinsic> for TransactionConverter {892 fn convert_transaction(893 &self,894 transaction: pallet_ethereum::Transaction,895 ) -> opaque::UncheckedExtrinsic {896 let extrinsic = UncheckedExtrinsic::new_unsigned(897 pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),898 );899 let encoded = extrinsic.encode();900 opaque::UncheckedExtrinsic::decode(&mut &encoded[..])901 .expect("Encoded extrinsic is always valid")902 }903}904905/// The address format for describing accounts.906pub type Address = sp_runtime::MultiAddress<AccountId, ()>;907/// Block header type as expected by this runtime.908pub type Header = generic::Header<BlockNumber, BlakeTwo256>;909/// Block type as expected by this runtime.910pub type Block = generic::Block<Header, UncheckedExtrinsic>;911/// A Block signed with a Justification912pub type SignedBlock = generic::SignedBlock<Block>;913/// BlockId type as expected by this runtime.914pub type BlockId = generic::BlockId<Block>;915/// The SignedExtension to the basic transaction logic.916pub type SignedExtra = (917 system::CheckSpecVersion<Runtime>,918 // system::CheckTxVersion<Runtime>,919 system::CheckGenesis<Runtime>,920 system::CheckEra<Runtime>,921 system::CheckNonce<Runtime>,922 system::CheckWeight<Runtime>,923 pallet_nft_charge_transaction::ChargeTransactionPayment<Runtime>,924 //pallet_contract_helpers::ContractHelpersExtension<Runtime>,925);926/// Unchecked extrinsic type as expected by this runtime.927pub type UncheckedExtrinsic =928 fp_self_contained::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;929/// Extrinsic type that has already been checked.930pub type CheckedExtrinsic = fp_self_contained::CheckedExtrinsic<AccountId, Call, SignedExtra, H160>;931/// Executive: handles dispatch to the various modules.932pub type Executive = frame_executive::Executive<933 Runtime,934 Block,935 frame_system::ChainContext<Runtime>,936 Runtime,937 AllPallets,938>;939940impl_opaque_keys! {941 pub struct SessionKeys {942 pub aura: Aura,943 }944}945946impl fp_self_contained::SelfContainedCall for Call {947 type SignedInfo = H160;948949 fn is_self_contained(&self) -> bool {950 match self {951 Call::Ethereum(call) => call.is_self_contained(),952 _ => false,953 }954 }955956 fn check_self_contained(&self) -> Option<Result<Self::SignedInfo, TransactionValidityError>> {957 match self {958 Call::Ethereum(call) => call.check_self_contained(),959 _ => None,960 }961 }962963 fn validate_self_contained(&self, info: &Self::SignedInfo) -> Option<TransactionValidity> {964 match self {965 Call::Ethereum(call) => call.validate_self_contained(info),966 _ => None,967 }968 }969970 fn pre_dispatch_self_contained(971 &self,972 info: &Self::SignedInfo,973 ) -> Option<Result<(), TransactionValidityError>> {974 match self {975 Call::Ethereum(call) => call.pre_dispatch_self_contained(info),976 _ => None,977 }978 }979980 fn apply_self_contained(981 self,982 info: Self::SignedInfo,983 ) -> Option<sp_runtime::DispatchResultWithInfo<PostDispatchInfoOf<Self>>> {984 match self {985 call @ Call::Ethereum(pallet_ethereum::Call::transact { .. }) => Some(call.dispatch(986 Origin::from(pallet_ethereum::RawOrigin::EthereumTransaction(info)),987 )),988 _ => None,989 }990 }991}992993macro_rules! dispatch_nft_runtime {994 ($collection:ident.$method:ident($($name:ident),*)) => {{995 use pallet_nft::dispatch::Dispatched;996997 let collection = Dispatched::dispatch(<pallet_common::CollectionHandle<Runtime>>::new($collection).unwrap());998 let dispatch = collection.as_dyn();9991000 dispatch.$method($($name),*)1001 }};1002}1003impl_runtime_apis! {1004 impl up_rpc::NftApi<Block, CrossAccountId, AccountId>1005 for Runtime1006 {1007 fn account_tokens(collection: CollectionId, account: CrossAccountId) -> Vec<TokenId> {1008 dispatch_nft_runtime!(collection.account_tokens(account))1009 }1010 fn token_exists(collection: CollectionId, token: TokenId) -> bool {1011 dispatch_nft_runtime!(collection.token_exists(token))1012 }10131014 fn token_owner(collection: CollectionId, token: TokenId) -> CrossAccountId {1015 dispatch_nft_runtime!(collection.token_owner(token))1016 }1017 fn const_metadata(collection: CollectionId, token: TokenId) -> Vec<u8> {1018 dispatch_nft_runtime!(collection.const_metadata(token))1019 }1020 fn variable_metadata(collection: CollectionId, token: TokenId) -> Vec<u8> {1021 dispatch_nft_runtime!(collection.variable_metadata(token))1022 }10231024 fn collection_tokens(collection: CollectionId) -> u32 {1025 dispatch_nft_runtime!(collection.collection_tokens())1026 }1027 fn account_balance(collection: CollectionId, account: CrossAccountId) -> u32 {1028 dispatch_nft_runtime!(collection.account_balance(account))1029 }1030 fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> u128 {1031 dispatch_nft_runtime!(collection.balance(account, token))1032 }1033 fn allowance(1034 collection: CollectionId,1035 sender: CrossAccountId,1036 spender: CrossAccountId,1037 token: TokenId,1038 ) -> u128 {1039 dispatch_nft_runtime!(collection.allowance(sender, spender, token))1040 }10411042 fn eth_contract_code(account: H160) -> Option<Vec<u8>> {1043 <pallet_nft::NftErcSupport<Runtime>>::get_code(&account)1044 .or_else(|| <pallet_evm_migration::OnMethodCall<Runtime>>::get_code(&account))1045 .or_else(|| <pallet_evm_contract_helpers::HelpersOnMethodCall<Self>>::get_code(&account))1046 }1047 fn adminlist(collection: CollectionId) -> Vec<CrossAccountId> {1048 <pallet_common::Pallet<Runtime>>::adminlist(collection)1049 }1050 fn allowlist(collection: CollectionId) -> Vec<CrossAccountId> {1051 <pallet_common::Pallet<Runtime>>::allowlist(collection)1052 }1053 fn allowed(collection: CollectionId, user: CrossAccountId) -> bool {1054 <pallet_common::Pallet<Runtime>>::allowed(collection, user)1055 }1056 fn last_token_id(collection: CollectionId) -> TokenId {1057 dispatch_nft_runtime!(collection.last_token_id())1058 }1059 fn collection_by_id(collection: CollectionId) -> Option<Collection<AccountId>> {1060 <pallet_common::CollectionById<Runtime>>::get(collection)1061 }1062 fn collection_stats() -> CollectionStats {1063 <pallet_common::Pallet<Runtime>>::collection_stats()1064 }1065 }10661067 impl sp_api::Core<Block> for Runtime {1068 fn version() -> RuntimeVersion {1069 VERSION1070 }10711072 fn execute_block(block: Block) {1073 Executive::execute_block(block)1074 }10751076 fn initialize_block(header: &<Block as BlockT>::Header) {1077 Executive::initialize_block(header)1078 }1079 }10801081 impl sp_api::Metadata<Block> for Runtime {1082 fn metadata() -> OpaqueMetadata {1083 OpaqueMetadata::new(Runtime::metadata().into())1084 }1085 }10861087 impl sp_block_builder::BlockBuilder<Block> for Runtime {1088 fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {1089 Executive::apply_extrinsic(extrinsic)1090 }10911092 fn finalize_block() -> <Block as BlockT>::Header {1093 Executive::finalize_block()1094 }10951096 fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {1097 data.create_extrinsics()1098 }10991100 fn check_inherents(1101 block: Block,1102 data: sp_inherents::InherentData,1103 ) -> sp_inherents::CheckInherentsResult {1104 data.check_extrinsics(&block)1105 }11061107 // fn random_seed() -> <Block as BlockT>::Hash {1108 // RandomnessCollectiveFlip::random_seed().01109 // }1110 }11111112 impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {1113 fn validate_transaction(1114 source: TransactionSource,1115 tx: <Block as BlockT>::Extrinsic,1116 hash: <Block as BlockT>::Hash,1117 ) -> TransactionValidity {1118 Executive::validate_transaction(source, tx, hash)1119 }1120 }11211122 impl sp_offchain::OffchainWorkerApi<Block> for Runtime {1123 fn offchain_worker(header: &<Block as BlockT>::Header) {1124 Executive::offchain_worker(header)1125 }1126 }11271128 impl fp_rpc::EthereumRuntimeRPCApi<Block> for Runtime {1129 fn chain_id() -> u64 {1130 <Runtime as pallet_evm::Config>::ChainId::get()1131 }11321133 fn account_basic(address: H160) -> EVMAccount {1134 EVM::account_basic(&address)1135 }11361137 fn gas_price() -> U256 {1138 <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price()1139 }11401141 fn account_code_at(address: H160) -> Vec<u8> {1142 EVM::account_codes(address)1143 }11441145 fn author() -> H160 {1146 <pallet_evm::Pallet<Runtime>>::find_author()1147 }11481149 fn storage_at(address: H160, index: U256) -> H256 {1150 let mut tmp = [0u8; 32];1151 index.to_big_endian(&mut tmp);1152 EVM::account_storages(address, H256::from_slice(&tmp[..]))1153 }11541155 fn call(1156 from: H160,1157 to: H160,1158 data: Vec<u8>,1159 value: U256,1160 gas_limit: U256,1161 gas_price: Option<U256>,1162 nonce: Option<U256>,1163 estimate: bool,1164 ) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {1165 let config = if estimate {1166 let mut config = <Runtime as pallet_evm::Config>::config().clone();1167 config.estimate = true;1168 Some(config)1169 } else {1170 None1171 };11721173 <Runtime as pallet_evm::Config>::Runner::call(1174 from,1175 to,1176 data,1177 value,1178 gas_limit.low_u64(),1179 gas_price,1180 nonce,1181 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),1182 ).map_err(|err| err.into())1183 }11841185 fn create(1186 from: H160,1187 data: Vec<u8>,1188 value: U256,1189 gas_limit: U256,1190 gas_price: Option<U256>,1191 nonce: Option<U256>,1192 estimate: bool,1193 ) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {1194 let config = if estimate {1195 let mut config = <Runtime as pallet_evm::Config>::config().clone();1196 config.estimate = true;1197 Some(config)1198 } else {1199 None1200 };12011202 <Runtime as pallet_evm::Config>::Runner::create(1203 from,1204 data,1205 value,1206 gas_limit.low_u64(),1207 gas_price,1208 nonce,1209 config.as_ref().unwrap_or_else(|| <Runtime as pallet_evm::Config>::config()),1210 ).map_err(|err| err.into())1211 }12121213 fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {1214 Ethereum::current_transaction_statuses()1215 }12161217 fn current_block() -> Option<pallet_ethereum::Block> {1218 Ethereum::current_block()1219 }12201221 fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {1222 Ethereum::current_receipts()1223 }12241225 fn current_all() -> (1226 Option<pallet_ethereum::Block>,1227 Option<Vec<pallet_ethereum::Receipt>>,1228 Option<Vec<TransactionStatus>>1229 ) {1230 (1231 Ethereum::current_block(),1232 Ethereum::current_receipts(),1233 Ethereum::current_transaction_statuses()1234 )1235 }12361237 fn extrinsic_filter(xts: Vec<<Block as sp_api::BlockT>::Extrinsic>) -> Vec<pallet_ethereum::Transaction> {1238 xts.into_iter().filter_map(|xt| match xt.0.function {1239 Call::Ethereum(pallet_ethereum::Call::transact { transaction }) => Some(transaction),1240 _ => None1241 }).collect()1242 }1243 }12441245 impl sp_session::SessionKeys<Block> for Runtime {1246 fn decode_session_keys(1247 encoded: Vec<u8>,1248 ) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {1249 SessionKeys::decode_into_raw_public_keys(&encoded)1250 }12511252 fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {1253 SessionKeys::generate(seed)1254 }1255 }12561257 impl sp_consensus_aura::AuraApi<Block, AuraId> for Runtime {1258 fn slot_duration() -> sp_consensus_aura::SlotDuration {1259 sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())1260 }12611262 fn authorities() -> Vec<AuraId> {1263 Aura::authorities().to_vec()1264 }1265 }12661267 impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime {1268 fn collect_collation_info() -> cumulus_primitives_core::CollationInfo {1269 ParachainSystem::collect_collation_info()1270 }1271 }12721273 impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {1274 fn account_nonce(account: AccountId) -> Index {1275 System::account_nonce(account)1276 }1277 }12781279 impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime {1280 fn query_info(uxt: <Block as BlockT>::Extrinsic, len: u32) -> RuntimeDispatchInfo<Balance> {1281 TransactionPayment::query_info(uxt, len)1282 }1283 fn query_fee_details(uxt: <Block as BlockT>::Extrinsic, len: u32) -> FeeDetails<Balance> {1284 TransactionPayment::query_fee_details(uxt, len)1285 }1286 }12871288 /*1289 impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash>1290 for Runtime1291 {1292 fn call(1293 origin: AccountId,1294 dest: AccountId,1295 value: Balance,1296 gas_limit: u64,1297 input_data: Vec<u8>,1298 ) -> pallet_contracts_primitives::ContractExecResult {1299 Contracts::bare_call(origin, dest, value, gas_limit, input_data, false)1300 }13011302 fn instantiate(1303 origin: AccountId,1304 endowment: Balance,1305 gas_limit: u64,1306 code: pallet_contracts_primitives::Code<Hash>,1307 data: Vec<u8>,1308 salt: Vec<u8>,1309 ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, BlockNumber>1310 {1311 Contracts::bare_instantiate(origin, endowment, gas_limit, code, data, salt, true, false)1312 }13131314 fn get_storage(1315 address: AccountId,1316 key: [u8; 32],1317 ) -> pallet_contracts_primitives::GetStorageResult {1318 Contracts::get_storage(address, key)1319 }13201321 fn rent_projection(1322 address: AccountId,1323 ) -> pallet_contracts_primitives::RentProjectionResult<BlockNumber> {1324 Contracts::rent_projection(address)1325 }1326 }1327 */13281329 #[cfg(feature = "runtime-benchmarks")]1330 impl frame_benchmarking::Benchmark<Block> for Runtime {1331 fn benchmark_metadata(extra: bool) -> (1332 Vec<frame_benchmarking::BenchmarkList>,1333 Vec<frame_support::traits::StorageInfo>,1334 ) {1335 use frame_benchmarking::{list_benchmark, Benchmarking, BenchmarkList};1336 use frame_support::traits::StorageInfoTrait;13371338 let mut list = Vec::<BenchmarkList>::new();13391340 list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);1341 list_benchmark!(list, extra, pallet_nft, Nft);1342 list_benchmark!(list, extra, pallet_inflation, Inflation);1343 list_benchmark!(list, extra, pallet_fungible, Fungible);1344 list_benchmark!(list, extra, pallet_refungible, Refungible);1345 list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);13461347 let storage_info = AllPalletsWithSystem::storage_info();13481349 return (list, storage_info)1350 }13511352 fn dispatch_benchmark(1353 config: frame_benchmarking::BenchmarkConfig1354 ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {1355 use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};13561357 let allowlist: Vec<TrackedStorageKey> = vec![1358 // Block Number1359 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),1360 // Total Issuance1361 hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),1362 // Execution Phase1363 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),1364 // Event Count1365 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),1366 // System Events1367 hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),1368 ];13691370 let mut batches = Vec::<BenchmarkBatch>::new();1371 let params = (&config, &allowlist);13721373 add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);1374 add_benchmark!(params, batches, pallet_nft, Nft);1375 add_benchmark!(params, batches, pallet_inflation, Inflation);1376 add_benchmark!(params, batches, pallet_fungible, Fungible);1377 add_benchmark!(params, batches, pallet_refungible, Refungible);1378 add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);13791380 if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }1381 Ok(batches)1382 }1383 }1384}13851386struct CheckInherents;13871388impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {1389 fn check_inherents(1390 block: &Block,1391 relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof,1392 ) -> sp_inherents::CheckInherentsResult {1393 let relay_chain_slot = relay_state_proof1394 .read_slot()1395 .expect("Could not read the relay chain slot from the proof");13961397 let inherent_data =1398 cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(1399 relay_chain_slot,1400 sp_std::time::Duration::from_secs(6),1401 )1402 .create_inherent_data()1403 .expect("Could not create the timestamp inherent data");14041405 inherent_data.check_extrinsics(block)1406 }1407}14081409cumulus_pallet_parachain_system::register_validate_block!(1410 Runtime = Runtime,1411 BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,1412 CheckInherents = CheckInherents,1413);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.abidiffbeforeafterboth--- a/tests/src/eth/marketplace/MarketPlaceKSM.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[{"internalType":"address","name":"_escrow","type":"address"}],"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":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"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":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"escrowBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"offers","outputs":[{"internalType":"uint256","name":"idNFT","type":"uint256"},{"internalType":"uint256","name":"currencyCode","type":"uint256"},{"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":"uint256","name":"_currencyCode","type":"uint256"},{"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":"_newEscrow","type":"address"}],"name":"setEscrow","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":"uint256","name":"_currencyCode","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
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;
+ //}
});
}