difftreelog
Merge pull request #158 from usetech-llc/feature/sponsorship-refactor
in: master
Refactor sponsorship logic to reduce cross-crate dependencies
24 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5113,6 +5113,7 @@
"nft-data-structs",
"pallet-aura",
"pallet-balances",
+ "pallet-contract-helpers",
"pallet-contracts",
"pallet-contracts-primitives",
"pallet-contracts-rpc-runtime-api",
@@ -5481,6 +5482,19 @@
]
[[package]]
+name = "pallet-contract-helpers"
+version = "0.1.0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "pallet-contracts",
+ "parity-scale-codec 2.1.3",
+ "sp-runtime",
+ "sp-std",
+ "up-sponsorship",
+]
+
+[[package]]
name = "pallet-contracts"
version = "3.0.0"
source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.3#c94e0cdfe5556680dca1996004751eeb114755d7"
@@ -5871,6 +5885,7 @@
"sp-io",
"sp-runtime",
"sp-std",
+ "up-sponsorship",
]
[[package]]
@@ -5880,13 +5895,8 @@
"frame-benchmarking",
"frame-support",
"frame-system",
- "nft-data-structs",
"pallet-balances",
- "pallet-contracts",
- "pallet-nft",
"pallet-nft-transaction-payment",
- "pallet-randomness-collective-flip",
- "pallet-timestamp",
"pallet-transaction-payment",
"parity-scale-codec 2.1.3",
"serde",
@@ -5903,12 +5913,6 @@
"frame-benchmarking",
"frame-support",
"frame-system",
- "nft-data-structs",
- "pallet-balances",
- "pallet-contracts",
- "pallet-nft",
- "pallet-randomness-collective-flip",
- "pallet-timestamp",
"pallet-transaction-payment",
"parity-scale-codec 2.1.3",
"serde",
@@ -5916,6 +5920,7 @@
"sp-io",
"sp-runtime",
"sp-std",
+ "up-sponsorship",
]
[[package]]
@@ -5996,10 +6001,6 @@
"frame-support",
"frame-system",
"log",
- "nft-data-structs",
- "pallet-contracts",
- "pallet-nft",
- "pallet-nft-transaction-payment",
"parity-scale-codec 2.1.3",
"serde",
"sp-core",
@@ -6007,6 +6008,7 @@
"sp-runtime",
"sp-std",
"substrate-test-utils",
+ "up-sponsorship",
]
[[package]]
@@ -11760,6 +11762,13 @@
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
+name = "up-sponsorship"
+version = "0.1.0"
+dependencies = [
+ "impl-trait-for-tuples 0.2.1",
+]
+
+[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,7 @@
members = [
'node/*',
'pallets/*',
- 'primitives',
+ 'primitives/*',
'runtime',
'crates/evm-coder',
'crates/evm-coder-macros',
node/cli/Cargo.tomldiffbeforeafterboth--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -284,7 +284,7 @@
version = '3.0.0'
[dependencies.nft-data-structs]
-path="../../primitives"
+path="../../primitives/nft"
default-features = false
################################################################################
pallets/contract-helpers/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/pallets/contract-helpers/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "pallet-contract-helpers"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[dependencies.up-sponsorship]
+default-features = false
+path = '../../primitives/sponsorship'
+version = '0.1.0'
+
+[dependencies]
+frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+
+[features]
+default = ["std"]
+std = [
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-contracts/std",
+ "sp-runtime/std",
+ "sp-std/std",
+]
\ No newline at end of file
pallets/contract-helpers/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/contract-helpers/src/lib.rs
@@ -0,0 +1,261 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use frame_support::sp_runtime::traits::StaticLookup;
+ use frame_support::{pallet_prelude::*, traits::IsSubType};
+ use frame_system::pallet_prelude::*;
+ use pallet_contracts::chain_extension::UncheckedFrom;
+ use sp_runtime::{
+ traits::{DispatchInfoOf, Hash, PostDispatchInfoOf, SignedExtension},
+ transaction_validity,
+ };
+ use sp_std::vec::Vec;
+ use up_sponsorship::SponsorshipHandler;
+
+ #[pallet::error]
+ pub enum Error<T> {
+ /// Should be contract owner
+ NoPermission,
+ }
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config + pallet_contracts::Config {}
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet<T>(_);
+
+ #[pallet::storage]
+ pub(super) type Owner<T: Config> = StorageMap<
+ Hasher = Twox128,
+ Key = T::AccountId,
+ Value = T::AccountId,
+ QueryKind = ValueQuery,
+ >;
+
+ #[pallet::storage]
+ pub(super) type AllowlistEnabled<T: Config> =
+ StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;
+
+ #[pallet::storage]
+ pub(super) type Allowlist<T: Config> = StorageDoubleMap<
+ Hasher1 = Twox128,
+ Key1 = T::AccountId,
+ Hasher2 = Twox64Concat,
+ Key2 = T::AccountId,
+ Value = bool,
+ QueryKind = ValueQuery,
+ >;
+
+ #[pallet::storage]
+ pub(super) type SelfSponsoring<T: Config> =
+ StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;
+
+ #[pallet::storage]
+ pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
+ Hasher = Twox128,
+ Key = T::AccountId,
+ Value = T::BlockNumber,
+ QueryKind = ValueQuery,
+ >;
+
+ #[pallet::storage]
+ pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<
+ Hasher1 = Twox128,
+ Key1 = T::AccountId,
+ Hasher2 = Twox128,
+ Key2 = T::AccountId,
+ Value = T::BlockNumber,
+ QueryKind = ValueQuery,
+ >;
+
+ #[pallet::call]
+ impl<T: Config> Pallet<T> {
+ #[pallet::weight(0)]
+ fn toggle_sponsoring(
+ origin: OriginFor<T>,
+ contract: T::AccountId,
+ sponsoring: bool,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ ensure!(<Owner<T>>::get(&contract) == sender, <Error<T>>::NoPermission);
+
+ if sponsoring {
+ <SelfSponsoring<T>>::insert(contract, true);
+ } else {
+ <SelfSponsoring<T>>::remove(contract);
+ }
+ Ok(())
+ }
+
+ #[pallet::weight(0)]
+ fn toggle_allowlist(
+ origin: OriginFor<T>,
+ contract: T::AccountId,
+ enabled: bool,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ ensure!(<Owner<T>>::get(&contract) == sender, <Error<T>>::NoPermission);
+
+ if enabled {
+ <AllowlistEnabled<T>>::insert(contract, true);
+ } else {
+ <AllowlistEnabled<T>>::remove(contract);
+ }
+ Ok(())
+ }
+
+ #[pallet::weight(0)]
+ fn toggle_allowed(
+ origin: OriginFor<T>,
+ contract: T::AccountId,
+ user: T::AccountId,
+ allowed: bool,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ ensure!(<Owner<T>>::get(&contract) == sender, <Error<T>>::NoPermission);
+
+ if allowed {
+ <Allowlist<T>>::insert(contract, user, true);
+ } else {
+ <Allowlist<T>>::remove(contract, user);
+ }
+ Ok(())
+ }
+
+ #[pallet::weight(0)]
+ fn set_sponsoring_rate_limit(
+ origin: OriginFor<T>,
+ contract: T::AccountId,
+ rate_limit: T::BlockNumber,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ ensure!(<Owner<T>>::get(&contract) == sender, <Error<T>>::NoPermission);
+
+ <SponsoringRateLimit<T>>::insert(contract, rate_limit);
+ Ok(())
+ }
+ }
+
+ #[derive(Encode, Decode, Clone, PartialEq, Eq)]
+ pub struct ContractHelpersExtension<T>(PhantomData<T>);
+ impl<T> core::fmt::Debug for ContractHelpersExtension<T> {
+ fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
+ fmt.debug_struct("ContractHelpersExtension").finish()
+ }
+ }
+
+ type CodeHash<T> = <T as frame_system::Config>::Hash;
+ impl<T> SignedExtension for ContractHelpersExtension<T>
+ where
+ T: Config + Send + Sync,
+ T::Call: sp_runtime::traits::Dispatchable,
+ T::Call: IsSubType<pallet_contracts::Call<T>>,
+ T::AccountId: UncheckedFrom<T::Hash>,
+ T::AccountId: AsRef<[u8]>,
+ {
+ const IDENTIFIER: &'static str = "ContractHelpers";
+ type AccountId = T::AccountId;
+ type Call = T::Call;
+ type AdditionalSigned = ();
+ type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;
+
+ fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {
+ Ok(())
+ }
+
+ fn validate(
+ &self,
+ who: &T::AccountId,
+ call: &Self::Call,
+ _info: &DispatchInfoOf<Self::Call>,
+ _len: usize,
+ ) -> transaction_validity::TransactionValidity {
+ match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
+ Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
+ let called_contract: T::AccountId =
+ T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());
+ if <AllowlistEnabled<T>>::get(&called_contract) {
+ if !<Allowlist<T>>::get(&called_contract, who)
+ && &<Owner<T>>::get(&called_contract) != who
+ {
+ return Err(transaction_validity::InvalidTransaction::Call.into());
+ }
+ }
+ }
+ _ => {}
+ }
+ Ok(transaction_validity::ValidTransaction::default())
+ }
+
+ fn pre_dispatch(
+ self,
+ who: &Self::AccountId,
+ call: &Self::Call,
+ _info: &DispatchInfoOf<Self::Call>,
+ _len: usize,
+ ) -> Result<Self::Pre, TransactionValidityError> {
+ match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
+ Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {
+ Ok(Some((who.clone(), code_hash.clone(), salt.clone())))
+ }
+ Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {
+ let code_hash = &T::Hashing::hash(&code);
+ Ok(Some((who.clone(), code_hash.clone(), salt.clone())))
+ }
+ _ => Ok(None),
+ }
+ }
+
+ fn post_dispatch(
+ pre: Self::Pre,
+ _info: &DispatchInfoOf<Self::Call>,
+ _post_info: &PostDispatchInfoOf<Self::Call>,
+ _len: usize,
+ _result: &DispatchResult,
+ ) -> Result<(), TransactionValidityError> {
+ if let Some((who, code_hash, salt)) = pre {
+ let new_contract_address =
+ <pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);
+ <Owner<T>>::insert(&new_contract_address, &who);
+ }
+
+ Ok(())
+ }
+ }
+
+ pub struct ContractSponsorshipHandler<T>(PhantomData<T>);
+ impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>
+ where
+ T: Config,
+ C: IsSubType<pallet_contracts::Call<T>>,
+ T::AccountId: UncheckedFrom<T::Hash>,
+ T::AccountId: AsRef<[u8]>,
+ {
+ fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+ match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
+ Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
+ let called_contract: T::AccountId =
+ T::Lookup::lookup((*dest).clone()).unwrap_or_default();
+ if <SelfSponsoring<T>>::get(&called_contract) {
+ let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);
+ let block_number =
+ <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);
+ let limit_time = last_tx_block + rate_limit;
+
+ if block_number >= limit_time {
+ SponsorBasket::<T>::insert(&called_contract, who, block_number);
+ return Some(called_contract);
+ }
+ }
+ }
+ _ => {}
+ }
+ None
+ }
+ }
+}
pallets/nft-charge-transaction/Cargo.tomldiffbeforeafterboth--- a/pallets/nft-charge-transaction/Cargo.toml
+++ b/pallets/nft-charge-transaction/Cargo.toml
@@ -23,19 +23,14 @@
frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
pallet-balances = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-timestamp = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
pallet-transaction-payment = { default-features = false, version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-randomness-collective-flip = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-benchmarking = { default-features = false, version = "3.0.0", optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-nft = { default-features = false, path="../nft" }
pallet-nft-transaction-payment = { default-features = false, path="../nft-transaction-payment" }
-nft-data-structs = { default-features = false, path="../../primitives", version = "0.9.0" }
[features]
default = ['std']
@@ -45,15 +40,10 @@
'frame-support/std',
'frame-system/std',
'pallet-balances/std',
- 'pallet-timestamp/std',
- 'pallet-randomness-collective-flip/std',
- 'pallet-contracts/std',
- 'pallet-nft/std',
'pallet-transaction-payment/std',
'pallet-nft-transaction-payment/std',
'sp-std/std',
'sp-runtime/std',
- 'nft-data-structs/std',
'frame-benchmarking/std',
]
runtime-benchmarks = ["frame-benchmarking"]
pallets/nft-charge-transaction/src/lib.rsdiffbeforeafterboth--- a/pallets/nft-charge-transaction/src/lib.rs
+++ b/pallets/nft-charge-transaction/src/lib.rs
@@ -14,16 +14,10 @@
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
-#[cfg(test)]
-mod tests;
-
use codec::{Decode, Encode};
-use frame_support::traits::{ Get};
+use frame_support::traits::Get;
use frame_support::{
decl_module, decl_storage,
- traits::{
- IsSubType,
- },
weights::{
DispatchInfo, PostDispatchInfo, DispatchClass
}
@@ -37,7 +31,6 @@
},
FixedPointOperand, DispatchResult
};
-use pallet_contracts::chain_extension::UncheckedFrom;
use pallet_transaction_payment::OnChargeTransaction;
use sp_std::prelude::*;
@@ -83,10 +76,8 @@
impl<T: Config> ChargeTransactionPayment<T>
where
- T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
+ T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo>,
BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
- T::AccountId: AsRef<[u8]>,
- T::AccountId: UncheckedFrom<T::Hash>,
{
fn traditional_fee(
len: usize,
@@ -134,13 +125,6 @@
.map(|i| (fee, i));
}
- // check errors
- let _error = <pallet_nft_transaction_payment::Module<T>>::check_error(who, call);
- match _error {
- Err(_error) => return Err(_error),
- Ok(_error) => {}
- };
-
// Determine who is paying transaction fee based on ecnomic model
// Parse call to extract collection ID and access collection sponsor
let sponsor = <pallet_nft_transaction_payment::Module<T>>::withdraw_type(who, call);
@@ -156,9 +140,7 @@
for ChargeTransactionPayment<T>
where
BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
- T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,
- T::AccountId: AsRef<[u8]>,
- T::AccountId: UncheckedFrom<T::Hash>,
+ T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo>,
{
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
type AccountId = T::AccountId;
@@ -209,7 +191,7 @@
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
let (tip, who, imbalance) = pre;
- let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(
+ let actual_fee = pallet_transaction_payment::Pallet::<T>::compute_actual_fee(
len as u32,
info,
post_info,
pallets/nft-transaction-payment/Cargo.tomldiffbeforeafterboth--- a/pallets/nft-transaction-payment/Cargo.toml
+++ b/pallets/nft-transaction-payment/Cargo.toml
@@ -22,19 +22,14 @@
serde = { version = "1.0.119", default-features = false }
frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-balances = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-timestamp = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
pallet-transaction-payment = { default-features = false, version = "3.0.0", git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-randomness-collective-flip = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-benchmarking = { default-features = false, version = "3.0.0", optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-nft = { default-features = false, path="../nft" }
-nft-data-structs = { default-features = false, path="../../primitives", version = "0.9.0" }
+up-sponsorship = { default-features = false, path = "../../primitives/sponsorship", version = "0.1.0" }
[features]
default = ['std']
@@ -43,15 +38,13 @@
'serde/std',
'frame-support/std',
'frame-system/std',
- 'pallet-balances/std',
- 'pallet-timestamp/std',
- 'pallet-randomness-collective-flip/std',
- 'pallet-contracts/std',
- 'pallet-nft/std',
+ 'sp-core/std',
+ 'sp-io/std',
'pallet-transaction-payment/std',
'sp-std/std',
'sp-runtime/std',
- 'nft-data-structs/std',
'frame-benchmarking/std',
+
+ 'up-sponsorship/std',
]
runtime-benchmarks = ["frame-benchmarking"]
pallets/nft-transaction-payment/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nft-transaction-payment/src/benchmarking.rs
+++ b/pallets/nft-transaction-payment/src/benchmarking.rs
@@ -1,7 +1,6 @@
#![cfg(feature = "runtime-benchmarks")]
use super::*;
-use crate::Module as NftTransactionPayment;
use sp_std::prelude::*;
use frame_system::RawOrigin;
pallets/nft-transaction-payment/src/lib.rsdiffbeforeafterboth--- a/pallets/nft-transaction-payment/src/lib.rs
+++ b/pallets/nft-transaction-payment/src/lib.rs
@@ -14,388 +14,32 @@
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
-#[cfg(test)]
-mod tests;
-
-use frame_support::{
- decl_error, decl_module, decl_storage,
- traits::{
- IsSubType,
- },
- weights::{
- DispatchInfo
- }
-};
-use sp_runtime::traits::StaticLookup;
-use sp_runtime::{
- traits::{
- Hash, Dispatchable,
- },
- transaction_validity::{
- InvalidTransaction, TransactionValidityError,
- },
-};
-use pallet_contracts::chain_extension::UncheckedFrom;
+use frame_support::{decl_module, decl_storage};
use sp_std::prelude::*;
-use nft_data_structs::{
- CreateItemData,
- CollectionId, CollectionMode, TokenId
-};
-
-type CodeHash<T> = <T as frame_system::Config>::Hash;
+use up_sponsorship::SponsorshipHandler;
- pub trait Config: frame_system::Config + pallet_contracts::Config + pallet_transaction_payment::Config + pallet_nft::Config {
- }
-
- // Error for non-fungible-token module.
-
- decl_error! {
- /// Error for non-fungible-token module.
- pub enum Error for Module<T: Config> {
- /// No available class ID
- NoAvailableClassId,
- /// No available token ID
- NoAvailableTokenId,
- /// Token(ClassId, TokenId) not found
- TokenNotFound,
- /// Class not found
- CollectionNotFound,
- /// The operator is not the owner of the token and has no permission
- NoPermission,
- /// Arithmetic calculation overflow
- NumOverflow,
- /// Can not destroy class
- /// Total issuance is not 0
- CannotDestroyClass,
- }
+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_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 check_error(
- who: &T::AccountId,
- call: &T::Call
- ) -> Result<bool, TransactionValidityError> where
- T::Call: Dispatchable<Info=DispatchInfo>,
- T::Call: IsSubType<pallet_nft::Call<T>>,
- T::Call: IsSubType<pallet_contracts::Call<T>>,
- T::AccountId: AsRef<[u8]>,
- T::AccountId: UncheckedFrom<T::Hash>
- {
-
- match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
- Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
-
- let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());
-
- let owned_contract = pallet_nft::ContractOwner::<T>::get(called_contract.clone()).as_ref() == Some(who);
- let white_list_enabled = pallet_nft::ContractWhiteListEnabled::<T>::contains_key(called_contract.clone());
-
- if !owned_contract && white_list_enabled {
- if !pallet_nft::ContractWhiteList::<T>::contains_key(called_contract.clone(), who) {
- return Err(InvalidTransaction::Call.into());
- }
- }
- Ok(true)
- },
- _ => { Ok(true) },
- }
- }
-
+impl<T: Config> Module<T> {
pub fn withdraw_type(
who: &T::AccountId,
call: &T::Call
- ) -> Option<T::AccountId> where
- T::Call: Dispatchable<Info=DispatchInfo>,
- T::Call: IsSubType<pallet_nft::Call<T>>,
- T::Call: IsSubType<pallet_contracts::Call<T>>,
- T::AccountId: AsRef<[u8]>,
- T::AccountId: UncheckedFrom<T::Hash>
- {
-
- let mut sponsor: Option<T::AccountId> = match IsSubType::<pallet_nft::Call<T>>::is_sub_type(call) {
- Some(pallet_nft::Call::create_item(collection_id, _owner, _properties)) => {
-
- Self::withdraw_create_item(who, collection_id, &_properties)
- },
- Some(pallet_nft::Call::transfer(_new_owner, collection_id, item_id, _value)) => {
-
- Self::withdraw_transfer(who, collection_id, item_id)
- },
- Some(pallet_nft::Call::set_variable_meta_data(collection_id, item_id, data)) => {
-
- Self::withdraw_set_variable_meta_data(collection_id, item_id, &data)
- },
- _ => None,
- };
-
- sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {
- Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {
-
- Self::withdraw_contract_call(who, dest)
- },
- Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {
-
- Self::withdraw_contract_instantiate(&who, code_hash, salt)
- },
- Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt)) => {
-
- Self::withdraw_contract_instantiate(&who, &T::Hashing::hash(&_code), _salt)
- },
- _ => None,
- });
-
- sponsor
- }
-
-
-
- pub fn withdraw_create_item(
- who: &T::AccountId,
- collection_id: &CollectionId,
- _properties: &CreateItemData,
) -> Option<T::AccountId> {
-
- let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
-
- // sponsor timeout
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
-
- let limit = collection.limits.sponsor_transfer_timeout;
- if pallet_nft::CreateItemBasket::<T>::contains_key((collection_id, &who)) {
- let last_tx_block = pallet_nft::CreateItemBasket::<T>::get((collection_id, &who));
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- return None;
- }
- }
- pallet_nft::CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
-
- // check free create limit
- if collection.limits.sponsored_data_size >= (_properties.len() as u32) {
- collection.sponsorship.sponsor()
- .cloned()
- } else {
- None
- }
- }
-
- pub fn withdraw_transfer(
- who: &T::AccountId,
- collection_id: &CollectionId,
- item_id: &TokenId,
- ) -> Option<T::AccountId> {
-
- let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
- let limits = pallet_nft::ChainLimit::get();
-
- let mut sponsor_transfer = false;
- if collection.sponsorship.confirmed() {
-
- let collection_limits = collection.limits.clone();
- let collection_mode = collection.mode.clone();
-
- // 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: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- limits.nft_sponsor_transfer_timeout
- };
-
- let mut sponsored = true;
- if pallet_nft::NftTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block = pallet_nft::NftTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- pallet_nft::NftTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
-
- sponsored
- }
- CollectionMode::Fungible(_) => {
-
- // get correct limit
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- limits.fungible_sponsor_transfer_timeout
- };
-
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let mut sponsored = true;
- if pallet_nft::FungibleTransferBasket::<T>::contains_key(collection_id, who) {
- let last_tx_block = pallet_nft::FungibleTransferBasket::<T>::get(collection_id, who);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- pallet_nft::FungibleTransferBasket::<T>::insert(collection_id, who, block_number);
- }
-
- sponsored
- }
- CollectionMode::ReFungible => {
-
- // get correct limit
- let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
- collection_limits.sponsor_transfer_timeout
- } else {
- limits.refungible_sponsor_transfer_timeout
- };
-
- let mut sponsored = true;
- if pallet_nft::ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {
- let last_tx_block = pallet_nft::ReFungibleTransferBasket::<T>::get(collection_id, item_id);
- let limit_time = last_tx_block + limit.into();
- if block_number <= limit_time {
- sponsored = false;
- }
- }
- if sponsored {
- pallet_nft::ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);
- }
-
- sponsored
- }
- _ => {
- false
- },
- };
- }
-
- if !sponsor_transfer {
- None
- } else {
- collection.sponsorship.sponsor()
- .cloned()
- }
- }
-
- pub fn withdraw_set_variable_meta_data(
- collection_id: &CollectionId,
- item_id: &TokenId,
- data: &Vec<u8>,
- ) -> Option<T::AccountId> {
-
- let mut sponsor_metadata_changes = false;
-
- let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;
-
- 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;
-
- if pallet_nft::VariableMetaDataBasket::<T>::get(collection_id, item_id)
- .map(|last_block| block_number - last_block > rate_limit)
- .unwrap_or(true)
- {
- sponsor_metadata_changes = true;
- pallet_nft::VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
- }
- }
- }
-
- if !sponsor_metadata_changes {
- None
- } else {
- collection.sponsorship.sponsor().cloned()
- }
-
- }
-
- pub fn withdraw_contract_call(
- who: &T::AccountId,
- dest: &<T::Lookup as StaticLookup>::Source
- ) -> Option<T::AccountId> {
-
- let called_contract: T::AccountId = T::Lookup::lookup((dest).clone()).unwrap_or(T::AccountId::default());
-
- let owned_contract = pallet_nft::ContractOwner::<T>::get(called_contract.clone()).as_ref() == Some(who);
- let white_list_enabled = pallet_nft::ContractWhiteListEnabled::<T>::contains_key(called_contract.clone());
-
- // ???
- if !owned_contract && white_list_enabled {
- if !pallet_nft::ContractWhiteList::<T>::contains_key(called_contract.clone(), who) {
- return Some(who.clone())
- // return Err(InvalidTransaction::Call.into());
- }
- }
-
- let mut sponsor_transfer = false;
- if pallet_nft::ContractSponsoringRateLimit::<T>::contains_key(called_contract.clone()) {
- let last_tx_block = pallet_nft::ContractSponsorBasket::<T>::get((&called_contract, &who));
- let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
- let rate_limit = pallet_nft::ContractSponsoringRateLimit::<T>::get(&called_contract);
- let limit_time = last_tx_block + rate_limit;
-
- if block_number >= limit_time {
- pallet_nft::ContractSponsorBasket::<T>::insert((called_contract.clone(), who.clone()), block_number);
- sponsor_transfer = true;
- }
- } else {
- sponsor_transfer = false;
- }
-
- if sponsor_transfer {
- if pallet_nft::ContractSelfSponsoring::<T>::contains_key(called_contract.clone()) {
- if pallet_nft::ContractSelfSponsoring::<T>::get(called_contract.clone()) {
- return Some(called_contract);
- }
- }
- }
-
- None
- }
-
- pub fn withdraw_contract_instantiate(
- who: &T::AccountId,
- code_hash: &CodeHash<T>,
- salt: &[u8],
- ) -> Option<T::AccountId> where
- T::AccountId: AsRef<[u8]>,
- T::AccountId: UncheckedFrom<T::Hash>
- {
-
- let new_contract_address = <pallet_contracts::Pallet<T>>::contract_address(
- &who,
- code_hash,
- salt,
- );
- pallet_nft::ContractOwner::<T>::insert(new_contract_address.clone(), who.clone());
-
- None
+ T::SponsorshipHandler::get_sponsor(who, call)
}
}
\ No newline at end of file
pallets/nft-transaction-payment/src/tests.rsdiffbeforeafterboth--- a/pallets/nft-transaction-payment/src/tests.rs
+++ /dev/null
@@ -1,241 +0,0 @@
-#[cfg(test)]
-mod tests {
- use crate as pallet_inflation;
-
- use frame_system;
- use frame_support::{traits::{Currency}, parameter_types};
- use frame_support::{traits::OnInitialize};
- use sp_core::H256;
- use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
-
- type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
- type Block = frame_system::mocking::MockBlock<Test>;
-
- const YEAR: u64 = 5_259_600;
-
- parameter_types! {
- pub const ExistentialDeposit: u64 = 1;
- pub const MaxLocks: u32 = 50;
- }
-
- impl pallet_balances::Config for Test {
- type AccountStore = System;
- type Balance = u64;
- type DustRemoval = ();
- type Event = ();
- type ExistentialDeposit = ExistentialDeposit;
- type WeightInfo = ();
- type MaxLocks = MaxLocks;
- }
-
- frame_support::construct_runtime!(
- pub enum Test where
- Block = Block,
- NodeBlock = Block,
- UncheckedExtrinsic = UncheckedExtrinsic,
- {
- Balances: pallet_balances::{Module, Call, Storage},
- System: frame_system::{Module, Call, Config, Storage, Event<T>},
- Inflation: pallet_inflation::{Module, Call, Storage},
- }
- );
-
- parameter_types! {
- pub const BlockHashCount: u64 = 250;
- pub BlockWeights: frame_system::limits::BlockWeights =
- frame_system::limits::BlockWeights::simple_max(1024);
- pub const SS58Prefix: u8 = 42;
- }
-
- impl frame_system::Config for Test {
- type BaseCallFilter = ();
- type BlockWeights = ();
- type BlockLength = ();
- type DbWeight = ();
- type Origin = Origin;
- type Call = Call;
- type Index = u64;
- type BlockNumber = u64;
- type Hash = H256;
- type Hashing = BlakeTwo256;
- type AccountId = u64;
- type Lookup = IdentityLookup<Self::AccountId>;
- type Header = Header;
- type Event = ();
- type BlockHashCount = BlockHashCount;
- type Version = ();
- type PalletInfo = PalletInfo;
- type AccountData = pallet_balances::AccountData<u64>;
- type OnNewAccount = ();
- type OnKilledAccount = ();
- type SystemWeightInfo = ();
- type SS58Prefix = SS58Prefix;
- }
-
- parameter_types! {
- pub TreasuryAccountId: u64 = 1234;
- pub const InflationBlockInterval: u32 = 100; // every time per how many blocks inflation is applied
- }
-
- impl pallet_inflation::Config for Test {
- type Currency = Balances;
- type TreasuryAccountId = TreasuryAccountId;
- type InflationBlockInterval = InflationBlockInterval;
- }
-
- // Build genesis storage according to the mock runtime.
- pub fn new_test_ext() -> sp_io::TestExternalities {
- frame_system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
- }
-
- #[test]
- fn inflation_works() {
- new_test_ext().execute_with(|| {
- // Total issuance = 1_000_000_000
- let initial_issuance: u64 = 1_000_000_000;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
-
- // BlockInflation should be set after 1st block and
- // first inflation deposit should be equal to BlockInflation
- Inflation::on_initialize(1);
- assert!(Inflation::block_inflation() > 0);
- assert_eq!(Balances::free_balance(1234) - initial_issuance, Inflation::block_inflation());
- });
- }
-
- #[test]
- fn inflation_second_deposit() {
- new_test_ext().execute_with(|| {
- // Total issuance = 1_000_000_000
- let initial_issuance: u64 = 1_000_000_000;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
- Inflation::on_initialize(1);
-
- // Next inflation deposit happens when block is multiple of InflationBlockInterval
- let mut block: u32 = 2;
- let balance_before: u64 = Balances::free_balance(1234);
- while block % InflationBlockInterval::get() != 0 {
- Inflation::on_initialize(block as u64);
- block += 1;
- }
- let balance_just_before: u64 = Balances::free_balance(1234);
- assert_eq!(balance_before, balance_just_before);
-
- // The block with inflation
- Inflation::on_initialize(block as u64);
- let balance_after: u64 = Balances::free_balance(1234);
- assert_eq!(balance_after - balance_just_before, Inflation::block_inflation());
- });
- }
-
- #[test]
- fn inflation_in_1_year() {
- new_test_ext().execute_with(|| {
- // Total issuance = 1_000_000_000
- let initial_issuance: u64 = 1_000_000_000;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
- Inflation::on_initialize(1);
- let block_inflation_year_0 = Inflation::block_inflation();
-
- Inflation::on_initialize(YEAR);
- let block_inflation_year_1 = Inflation::block_inflation();
-
- // Assert that year 1 inflation is less than year 0
- assert!(block_inflation_year_0 > block_inflation_year_1);
- });
- }
-
- #[test]
- fn inflation_in_1_to_9_years() {
- new_test_ext().execute_with(|| {
- // Total issuance = 1_000_000_000
- let initial_issuance: u64 = 1_000_000_000;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
- Inflation::on_initialize(1);
-
- for year in 1..=9 {
- let block_inflation_year_before = Inflation::block_inflation();
- Inflation::on_initialize(YEAR * year);
- let block_inflation_year_after = Inflation::block_inflation();
-
- // Assert that next year inflation is less than previous year inflation
- assert!(block_inflation_year_before > block_inflation_year_after);
- }
-
- });
- }
-
- #[test]
- fn inflation_after_year_10_is_flat() {
- new_test_ext().execute_with(|| {
- // Total issuance = 1_000_000_000
- let initial_issuance: u64 = 1_000_000_000;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
- Inflation::on_initialize(YEAR * 9);
-
- for year in 10..=20 {
- let block_inflation_year_before = Inflation::block_inflation();
- Inflation::on_initialize(YEAR * year);
- let block_inflation_year_after = Inflation::block_inflation();
-
- // Assert that next year inflation is equal to previous year inflation
- assert_eq!(block_inflation_year_before, block_inflation_year_after);
- }
- });
- }
-
- #[test]
- fn inflation_rate_by_year() {
- new_test_ext().execute_with(|| {
- let payouts: u64 = YEAR / InflationBlockInterval::get() as u64;
-
- // Inflation starts at 10% and does down by 2/3% every year until year 9 (included),
- // then it is flat.
- let payout_by_year: [u64; 11] = [
- 1000,
- 933,
- 867,
- 800,
- 733,
- 667,
- 600,
- 533,
- 467,
- 400,
- 400
- ];
-
- // For accuracy total issuance = payout0 * payouts * 10;
- let initial_issuance: u64 = payout_by_year[0] * payouts * 10;
- let _ = <Balances as Currency<_>>::deposit_creating(&1234, initial_issuance);
- assert_eq!(Balances::free_balance(1234), initial_issuance);
-
- for year in 0..=10 {
- // Year first block
- Inflation::on_initialize(year*YEAR);
- let mut actual_payout = Inflation::block_inflation();
- assert_eq!(actual_payout, payout_by_year[year as usize]);
-
- // Year second block
- Inflation::on_initialize(year*YEAR+1);
- actual_payout = Inflation::block_inflation();
- assert_eq!(actual_payout, payout_by_year[year as usize]);
-
- // Year middle block
- Inflation::on_initialize(year*YEAR + YEAR/2);
- actual_payout = Inflation::block_inflation();
- assert_eq!(actual_payout, payout_by_year[year as usize]);
-
- // Year last block
- Inflation::on_initialize((year + 1)*YEAR-1);
- actual_payout = Inflation::block_inflation();
- assert_eq!(actual_payout, payout_by_year[year as usize]);
- }
- });
- }
-}
pallets/nft/Cargo.tomldiffbeforeafterboth--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -30,6 +30,7 @@
'pallet-transaction-payment/std',
'fp-evm/std',
'nft-data-structs/std',
+ 'up-sponsorship/std',
'sp-std/std',
'sp-api/std',
'sp-runtime/std',
@@ -135,9 +136,14 @@
[dependencies.nft-data-structs]
default-features = false
-path = '../../primitives'
+path = '../../primitives/nft'
version = '0.9.0'
+[dependencies.up-sponsorship]
+default-features = false
+path = '../../primitives/sponsorship'
+version = '0.1.0'
+
[dependencies]
ethereum-tx-sign = { version = "3.0.4", optional = true }
pallets/nft/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#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910extern crate alloc;1112pub use serde::{Serialize, Deserialize};1314pub use frame_support::{15 construct_runtime, decl_event, decl_module, decl_storage, decl_error,16 dispatch::DispatchResult,17 ensure, fail, parameter_types,18 traits::{19 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,20 Randomness, IsSubType, WithdrawReasons,21 },22 weights::{23 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},24 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,25 WeightToFeePolynomial, DispatchClass,26 },27 StorageValue,28 transactional,29};3031use frame_system::{self as system, ensure_signed, ensure_root};32use sp_core::H160;33use sp_runtime::sp_std::prelude::Vec;34use core::ops::{Deref, DerefMut};35use core::cell::RefCell;36use nft_data_structs::{37 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,38 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits,39 CollectionId, CollectionMode, TokenId, 40 SchemaVersion, SponsorshipState, Ownership,41 NftItemType, FungibleItemType, ReFungibleItemType42};43use pallet_ethereum::EthereumTransactionSender;4445#[cfg(test)]46mod mock;4748#[cfg(test)]49mod tests;5051mod default_weights;52mod eth;5354pub use eth::NftErcSupport;55pub use eth::account::*;56use eth::erc::{ERC20Events, ERC721Events};5758#[cfg(feature = "runtime-benchmarks")]59mod benchmarking;6061pub trait WeightInfo {62 fn create_collection() -> Weight;63 fn destroy_collection() -> Weight;64 fn add_to_white_list() -> Weight;65 fn remove_from_white_list() -> Weight;66 fn set_public_access_mode() -> Weight;67 fn set_mint_permission() -> Weight;68 fn change_collection_owner() -> Weight;69 fn add_collection_admin() -> Weight;70 fn remove_collection_admin() -> Weight;71 fn set_collection_sponsor() -> Weight;72 fn confirm_sponsorship() -> Weight;73 fn remove_collection_sponsor() -> Weight;74 fn create_item(s: usize) -> Weight;75 fn burn_item() -> Weight;76 fn transfer() -> Weight;77 fn approve() -> Weight;78 fn transfer_from() -> Weight;79 fn set_offchain_schema() -> Weight;80 fn set_const_on_chain_schema() -> Weight;81 fn set_variable_on_chain_schema() -> Weight;82 fn set_variable_meta_data() -> Weight;83 fn enable_contract_sponsoring() -> Weight;84 fn set_schema_version() -> Weight;85 fn set_chain_limits() -> Weight;86 fn set_contract_sponsoring_rate_limit() -> Weight;87 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;88 fn toggle_contract_white_list() -> Weight;89 fn add_to_contract_white_list() -> Weight;90 fn remove_from_contract_white_list() -> Weight;91 fn set_collection_limits() -> Weight;92}9394decl_error! {95 /// Error for non-fungible-token module.96 pub enum Error for Module<T: Config> {97 /// Total collections bound exceeded.98 TotalCollectionsLimitExceeded,99 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.100 CollectionDecimalPointLimitExceeded, 101 /// Collection name can not be longer than 63 char.102 CollectionNameLimitExceeded, 103 /// Collection description can not be longer than 255 char.104 CollectionDescriptionLimitExceeded, 105 /// Token prefix can not be longer than 15 char.106 CollectionTokenPrefixLimitExceeded,107 /// This collection does not exist.108 CollectionNotFound,109 /// Item not exists.110 TokenNotFound,111 /// Admin not found112 AdminNotFound,113 /// Arithmetic calculation overflow.114 NumOverflow, 115 /// Account already has admin role.116 AlreadyAdmin, 117 /// You do not own this collection.118 NoPermission,119 /// This address is not set as sponsor, use setCollectionSponsor first.120 ConfirmUnsetSponsorFail,121 /// Collection is not in mint mode.122 PublicMintingNotAllowed,123 /// Sender parameter and item owner must be equal.124 MustBeTokenOwner,125 /// Item balance not enough.126 TokenValueTooLow,127 /// Size of item is too large.128 NftSizeLimitExceeded,129 /// No approve found130 ApproveNotFound,131 /// Requested value more than approved.132 TokenValueNotEnough,133 /// Only approved addresses can call this method.134 ApproveRequired,135 /// Address is not in white list.136 AddresNotInWhiteList,137 /// Number of collection admins bound exceeded.138 CollectionAdminsLimitExceeded,139 /// Owned tokens by a single address bound exceeded.140 AddressOwnershipLimitExceeded,141 /// Length of items properties must be greater than 0.142 EmptyArgument,143 /// const_data exceeded data limit.144 TokenConstDataLimitExceeded,145 /// variable_data exceeded data limit.146 TokenVariableDataLimitExceeded,147 /// Not NFT item data used to mint in NFT collection.148 NotNftDataUsedToMintNftCollectionToken,149 /// Not Fungible item data used to mint in Fungible collection.150 NotFungibleDataUsedToMintFungibleCollectionToken,151 /// Not Re Fungible item data used to mint in Re Fungible collection.152 NotReFungibleDataUsedToMintReFungibleCollectionToken,153 /// Unexpected collection type.154 UnexpectedCollectionType,155 /// Can't store metadata in fungible tokens.156 CantStoreMetadataInFungibleTokens,157 /// Collection token limit exceeded158 CollectionTokenLimitExceeded,159 /// Account token limit exceeded per collection160 AccountTokenLimitExceeded,161 /// Collection limit bounds per collection exceeded162 CollectionLimitBoundsExceeded,163 /// Tried to enable permissions which are only permitted to be disabled164 OwnerPermissionsCantBeReverted,165 /// Schema data size limit bound exceeded166 SchemaDataLimitExceeded,167 /// Maximum refungibility exceeded168 WrongRefungiblePieces,169 /// createRefungible should be called with one owner170 BadCreateRefungibleCall,171 /// Gas limit exceeded172 OutOfGas,173 }174}175176pub struct CollectionHandle<T: Config> {177 pub id: CollectionId,178 collection: Collection<T>,179 logs: eth::log::LogRecorder,180 evm_address: H160,181 gas_limit: RefCell<u64>,182}183impl<T: Config> CollectionHandle<T> {184 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {185 <CollectionById<T>>::get(id)186 .map(|collection| Self {187 id,188 collection,189 logs: eth::log::LogRecorder::default(),190 evm_address: eth::collection_id_to_address(id),191 gas_limit: RefCell::new(gas_limit),192 })193 }194 pub fn get(id: CollectionId) -> Option<Self> {195 Self::get_with_gas_limit(id, u64::MAX)196 }197 pub fn gas_left(&self) -> u64 {198 *self.gas_limit.borrow()199 }200 pub fn consume_gas(&self, gas: u64) -> DispatchResult {201 let mut gas_limit = self.gas_limit.borrow_mut();202 if *gas_limit < gas {203 fail!(Error::<T>::OutOfGas);204 }205 *gas_limit -= gas;206 Ok(())207 }208 pub fn log(&self, log: impl evm_coder::ToLog) {209 self.logs.log(log.to_log(self.evm_address))210 }211 pub fn into_inner(self) -> Collection<T> {212 self.collection.clone()213 }214}215impl<T: Config> Deref for CollectionHandle<T> {216 type Target = Collection<T>;217218 fn deref(&self) -> &Self::Target {219 &self.collection220 }221}222223impl<T: Config> DerefMut for CollectionHandle<T> {224 fn deref_mut(&mut self) -> &mut Self::Target {225 &mut self.collection226 }227}228229pub trait Config: system::Config + Sized {230 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;231232 /// Weight information for extrinsics in this pallet.233 type WeightInfo: WeightInfo;234235 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;236 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;237 type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;238239 type CrossAccountId: CrossAccountId<Self::AccountId>;240 type Currency: Currency<Self::AccountId>;241 type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;242 type TreasuryAccountId: Get<Self::AccountId>;243244 type EthereumChainId: Get<u64>;245 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;246}247248// # Used definitions249//250// ## User control levels251//252// chain-controlled - key is uncontrolled by user253// i.e autoincrementing index254// can use non-cryptographic hash255// real - key is controlled by user256// but it is hard to generate enough colliding values, i.e owner of signed txs257// can use non-cryptographic hash258// controlled - key is completly controlled by users259// i.e maps with mutable keys260// should use cryptographic hash261//262// ## User control level downgrade reasons263//264// ?1 - chain-controlled -> controlled265// collections/tokens can be destroyed, resulting in massive holes266// ?2 - chain-controlled -> controlled267// same as ?1, but can be only added, resulting in easier exploitation268// ?3 - real -> controlled269// no confirmation required, so addresses can be easily generated270decl_storage! {271 trait Store for Module<T: Config> as Nft {272273 //#region Private members274 /// Id of next collection275 CreatedCollectionCount: u32;276 /// Used for migrations277 ChainVersion: u64;278 /// Id of last collection token279 /// Collection id (controlled?1)280 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;281 //#endregion282283 //#region Chain limits struct284 pub ChainLimit get(fn chain_limit) config(): ChainLimits;285 //#endregion286287 //#region Bound counters288 /// Amount of collections destroyed, used for total amount tracking with289 /// CreatedCollectionCount290 DestroyedCollectionCount: u32;291 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)292 /// Account id (real)293 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294 //#endregion295296 //#region Basic collections297 /// Collection info298 /// Collection id (controlled?1)299 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300 /// List of collection admins301 /// Collection id (controlled?2)302 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303 /// Whitelisted collection users304 /// Collection id (controlled?2), user id (controlled?3)305 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306 //#endregion307308 /// How many of collection items user have309 /// Collection id (controlled?2), account id (real)310 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312 /// Amount of items which spender can transfer out of owners account (via transferFrom)313 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))314 /// TODO: Off chain worker should remove from this map when token gets removed315 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317 //#region Item collections318 /// Collection id (controlled?2), token id (controlled?1)319 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320 /// Collection id (controlled?2), owner (controlled?2)321 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322 /// Collection id (controlled?2), token id (controlled?1)323 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324 //#endregion325326 //#region Index list327 /// Collection id (controlled?2), tokens owner (controlled?2)328 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329 //#endregion330331 //#region Tokens transfer rate limit baskets332 /// (Collection id (controlled?2), who created (real))333 /// TODO: Off chain worker should remove from this map when collection gets removed334 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335 /// Collection id (controlled?2), token id (controlled?2)336 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337 /// Collection id (controlled?2), owning user (real)338 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339 /// Collection id (controlled?2), token id (controlled?2)340 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341 //#endregion342343 /// Variable metadata sponsoring344 /// Collection id (controlled?2), token id (controlled?2)345 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346 347 //#region Contract Sponsorship and Ownership348 /// Contract address (real)349 pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;350 /// Contract address (real)351 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;352 /// (Contract address(real), caller (real))353 pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;354 /// Contract address (real)355 pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;356 /// Contract address (real)357 pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 358 /// Contract address (real) => Whitelisted user (controlled?3)359 pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 360 //#endregion361 }362 add_extra_genesis {363 build(|config: &GenesisConfig<T>| {364 // Modification of storage365 for (_num, _c) in &config.collection_id {366 <Module<T>>::init_collection(_c);367 }368369 for (_num, _c, _i) in &config.nft_item_id {370 <Module<T>>::init_nft_token(*_c, _i);371 }372373 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {374 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);375 }376377 for (_num, _c, _i) in &config.refungible_item_id {378 <Module<T>>::init_refungible_token(*_c, _i);379 }380 })381 }382}383384decl_event!(385 pub enum Event<T>386 where387 AccountId = <T as frame_system::Config>::AccountId,388 CrossAccountId = <T as Config>::CrossAccountId,389 {390 /// New collection was created391 /// 392 /// # Arguments393 /// 394 /// * collection_id: Globally unique identifier of newly created collection.395 /// 396 /// * mode: [CollectionMode] converted into u8.397 /// 398 /// * account_id: Collection owner.399 CollectionCreated(CollectionId, u8, AccountId),400401 /// New item was created.402 /// 403 /// # Arguments404 /// 405 /// * collection_id: Id of the collection where item was created.406 /// 407 /// * item_id: Id of an item. Unique within the collection.408 ///409 /// * recipient: Owner of newly created item 410 ItemCreated(CollectionId, TokenId, CrossAccountId),411412 /// Collection item was burned.413 /// 414 /// # Arguments415 /// 416 /// collection_id.417 /// 418 /// item_id: Identifier of burned NFT.419 ItemDestroyed(CollectionId, TokenId),420421 /// Item was transferred422 ///423 /// * collection_id: Id of collection to which item is belong424 ///425 /// * item_id: Id of an item426 ///427 /// * sender: Original owner of item428 ///429 /// * recipient: New owner of item430 ///431 /// * amount: Always 1 for NFT432 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),433434 /// * collection_id435 ///436 /// * item_id437 ///438 /// * sender439 ///440 /// * spender441 ///442 /// * amount443 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),444 }445);446447decl_module! {448 pub struct Module<T: Config> for enum Call 449 where 450 origin: T::Origin451 {452 fn deposit_event() = default;453 type Error = Error<T>;454455 fn on_initialize(_now: T::BlockNumber) -> Weight {456 0457 }458459 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.460 /// 461 /// # Permissions462 /// 463 /// * Anyone.464 /// 465 /// # Arguments466 /// 467 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.468 /// 469 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.470 /// 471 /// * token_prefix: UTF-8 string with token prefix.472 /// 473 /// * mode: [CollectionMode] collection type and type dependent data.474 // returns collection ID475 #[weight = <T as Config>::WeightInfo::create_collection()]476 #[transactional]477 pub fn create_collection(origin,478 collection_name: Vec<u16>,479 collection_description: Vec<u16>,480 token_prefix: Vec<u8>,481 mode: CollectionMode) -> DispatchResult {482483 // Anyone can create a collection484 let who = ensure_signed(origin)?;485486 // Take a (non-refundable) deposit of collection creation487 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();488 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(489 &T::TreasuryAccountId::get(),490 T::CollectionCreationPrice::get(),491 ));492 <T as Config>::Currency::settle(493 &who,494 imbalance,495 WithdrawReasons::TRANSFER,496 ExistenceRequirement::KeepAlive,497 ).map_err(|_| Error::<T>::NoPermission)?;498499 let decimal_points = match mode {500 CollectionMode::Fungible(points) => points,501 _ => 0502 };503504 let chain_limit = ChainLimit::get();505506 let created_count = CreatedCollectionCount::get();507 let destroyed_count = DestroyedCollectionCount::get();508509 // bound Total number of collections510 ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);511512 // check params513 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);514 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);515 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);516 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);517518 // Generate next collection ID519 let next_id = created_count520 .checked_add(1)521 .ok_or(Error::<T>::NumOverflow)?;522523 CreatedCollectionCount::put(next_id);524525 let limits = CollectionLimits {526 sponsored_data_size: chain_limit.custom_data_limit,527 ..Default::default()528 };529530 // Create new collection531 let new_collection = Collection {532 owner: who.clone(),533 name: collection_name,534 mode: mode.clone(),535 mint_mode: false,536 access: AccessMode::Normal,537 description: collection_description,538 decimal_points: decimal_points,539 token_prefix: token_prefix,540 offchain_schema: Vec::new(),541 schema_version: SchemaVersion::ImageURL,542 sponsorship: SponsorshipState::Disabled,543 variable_on_chain_schema: Vec::new(),544 const_on_chain_schema: Vec::new(),545 limits,546 };547548 // Add new collection to map549 <CollectionById<T>>::insert(next_id, new_collection);550551 // call event552 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));553554 Ok(())555 }556557 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.558 /// 559 /// # Permissions560 /// 561 /// * Collection Owner.562 /// 563 /// # Arguments564 /// 565 /// * collection_id: collection to destroy.566 #[weight = <T as Config>::WeightInfo::destroy_collection()]567 #[transactional]568 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {569570 let sender = ensure_signed(origin)?;571 let collection = Self::get_collection(collection_id)?;572 Self::check_owner_permissions(&collection, &sender)?;573 if !collection.limits.owner_can_destroy {574 fail!(Error::<T>::NoPermission);575 }576577 <AddressTokens<T>>::remove_prefix(collection_id);578 <Allowances<T>>::remove_prefix(collection_id);579 <Balance<T>>::remove_prefix(collection_id);580 <ItemListIndex>::remove(collection_id);581 <AdminList<T>>::remove(collection_id);582 <CollectionById<T>>::remove(collection_id);583 <WhiteList<T>>::remove_prefix(collection_id);584585 <NftItemList<T>>::remove_prefix(collection_id);586 <FungibleItemList<T>>::remove_prefix(collection_id);587 <ReFungibleItemList<T>>::remove_prefix(collection_id);588589 <NftTransferBasket<T>>::remove_prefix(collection_id);590 <FungibleTransferBasket<T>>::remove_prefix(collection_id);591 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);592593 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);594595 DestroyedCollectionCount::put(DestroyedCollectionCount::get()596 .checked_add(1)597 .ok_or(Error::<T>::NumOverflow)?);598599 Ok(())600 }601602 /// Add an address to white list.603 /// 604 /// # Permissions605 /// 606 /// * Collection Owner607 /// * Collection Admin608 /// 609 /// # Arguments610 /// 611 /// * collection_id.612 /// 613 /// * address.614 #[weight = <T as Config>::WeightInfo::add_to_white_list()]615 #[transactional]616 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{617618 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);619 let collection = Self::get_collection(collection_id)?;620621 Self::toggle_white_list_internal(622 &sender,623 &collection,624 &address,625 true,626 )?;627628 Ok(())629 }630631 /// Remove an address from white list.632 /// 633 /// # Permissions634 /// 635 /// * Collection Owner636 /// * Collection Admin637 /// 638 /// # Arguments639 /// 640 /// * collection_id.641 /// 642 /// * address.643 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]644 #[transactional]645 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{646647 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);648 let collection = Self::get_collection(collection_id)?;649650 Self::toggle_white_list_internal(651 &sender,652 &collection,653 &address,654 false,655 )?;656657 Ok(())658 }659660 /// Toggle between normal and white list access for the methods with access for `Anyone`.661 /// 662 /// # Permissions663 /// 664 /// * Collection Owner.665 /// 666 /// # Arguments667 /// 668 /// * collection_id.669 /// 670 /// * mode: [AccessMode]671 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]672 #[transactional]673 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult674 {675 let sender = ensure_signed(origin)?;676677 let mut target_collection = Self::get_collection(collection_id)?;678 Self::check_owner_permissions(&target_collection, &sender)?;679 target_collection.access = mode;680 Self::save_collection(target_collection);681682 Ok(())683 }684685 /// Allows Anyone to create tokens if:686 /// * White List is enabled, and687 /// * Address is added to white list, and688 /// * This method was called with True parameter689 /// 690 /// # Permissions691 /// * Collection Owner692 ///693 /// # Arguments694 /// 695 /// * collection_id.696 /// 697 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.698 #[weight = <T as Config>::WeightInfo::set_mint_permission()]699 #[transactional]700 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult701 {702 let sender = ensure_signed(origin)?;703704 let mut target_collection = Self::get_collection(collection_id)?;705 Self::check_owner_permissions(&target_collection, &sender)?;706 target_collection.mint_mode = mint_permission;707 Self::save_collection(target_collection);708709 Ok(())710 }711712 /// Change the owner of the collection.713 /// 714 /// # Permissions715 /// 716 /// * Collection Owner.717 /// 718 /// # Arguments719 /// 720 /// * collection_id.721 /// 722 /// * new_owner.723 #[weight = <T as Config>::WeightInfo::change_collection_owner()]724 #[transactional]725 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {726727 let sender = ensure_signed(origin)?;728 let mut target_collection = Self::get_collection(collection_id)?;729 Self::check_owner_permissions(&target_collection, &sender)?;730 target_collection.owner = new_owner;731 Self::save_collection(target_collection);732733 Ok(())734 }735736 /// Adds an admin of the Collection.737 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership. 738 /// 739 /// # Permissions740 /// 741 /// * Collection Owner.742 /// * Collection Admin.743 /// 744 /// # Arguments745 /// 746 /// * collection_id: ID of the Collection to add admin for.747 /// 748 /// * new_admin_id: Address of new admin to add.749 #[weight = <T as Config>::WeightInfo::add_collection_admin()]750 #[transactional]751 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {752 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);753 let collection = Self::get_collection(collection_id)?;754 Self::check_owner_or_admin_permissions(&collection, &sender)?;755 let mut admin_arr = <AdminList<T>>::get(collection_id);756757 match admin_arr.binary_search(&new_admin_id) {758 Ok(_) => {},759 Err(idx) => {760 let limits = ChainLimit::get();761 ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);762 admin_arr.insert(idx, new_admin_id);763 <AdminList<T>>::insert(collection_id, admin_arr);764 }765 }766 Ok(())767 }768769 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.770 ///771 /// # Permissions772 /// 773 /// * Collection Owner.774 /// * Collection Admin.775 /// 776 /// # Arguments777 /// 778 /// * collection_id: ID of the Collection to remove admin for.779 /// 780 /// * account_id: Address of admin to remove.781 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]782 #[transactional]783 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {784 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);785 let collection = Self::get_collection(collection_id)?;786 Self::check_owner_or_admin_permissions(&collection, &sender)?;787 let mut admin_arr = <AdminList<T>>::get(collection_id);788789 match admin_arr.binary_search(&account_id) {790 Ok(idx) => {791 admin_arr.remove(idx);792 <AdminList<T>>::insert(collection_id, admin_arr);793 },794 Err(_) => {}795 }796 Ok(())797 }798799 /// # Permissions800 /// 801 /// * Collection Owner802 /// 803 /// # Arguments804 /// 805 /// * collection_id.806 /// 807 /// * new_sponsor.808 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]809 #[transactional]810 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {811 let sender = ensure_signed(origin)?;812 let mut target_collection = Self::get_collection(collection_id)?;813 Self::check_owner_permissions(&target_collection, &sender)?;814815 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);816 Self::save_collection(target_collection);817818 Ok(())819 }820821 /// # Permissions822 /// 823 /// * Sponsor.824 /// 825 /// # Arguments826 /// 827 /// * collection_id.828 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]829 #[transactional]830 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {831 let sender = ensure_signed(origin)?;832833 let mut target_collection = Self::get_collection(collection_id)?;834 ensure!(835 target_collection.sponsorship.pending_sponsor() == Some(&sender),836 Error::<T>::ConfirmUnsetSponsorFail837 );838839 target_collection.sponsorship = SponsorshipState::Confirmed(sender);840 Self::save_collection(target_collection);841842 Ok(())843 }844845 /// Switch back to pay-per-own-transaction model.846 ///847 /// # Permissions848 ///849 /// * Collection owner.850 /// 851 /// # Arguments852 /// 853 /// * collection_id.854 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]855 #[transactional]856 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {857 let sender = ensure_signed(origin)?;858859 let mut target_collection = Self::get_collection(collection_id)?;860 Self::check_owner_permissions(&target_collection, &sender)?;861862 target_collection.sponsorship = SponsorshipState::Disabled;863 Self::save_collection(target_collection);864865 Ok(())866 }867868 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.869 /// 870 /// # Permissions871 /// 872 /// * Collection Owner.873 /// * Collection Admin.874 /// * Anyone if875 /// * White List is enabled, and876 /// * Address is added to white list, and877 /// * MintPermission is enabled (see SetMintPermission method)878 /// 879 /// # Arguments880 /// 881 /// * collection_id: ID of the collection.882 /// 883 /// * owner: Address, initial owner of the NFT.884 ///885 /// * data: Token data to store on chain.886 // #[weight =887 // (130_000_000 as Weight)888 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))889 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))890 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]891892 #[weight = <T as Config>::WeightInfo::create_item(data.len())]893 #[transactional]894 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897898 Self::create_item_internal(&sender, &collection, &owner, data)?;899900 Self::submit_logs(collection)?;901 Ok(())902 }903904 /// This method creates multiple items in a collection created with CreateCollection method.905 /// 906 /// # Permissions907 /// 908 /// * Collection Owner.909 /// * Collection Admin.910 /// * Anyone if911 /// * White List is enabled, and912 /// * Address is added to white list, and913 /// * MintPermission is enabled (see SetMintPermission method)914 /// 915 /// # Arguments916 /// 917 /// * collection_id: ID of the collection.918 /// 919 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].920 /// 921 /// * owner: Address, initial owner of the NFT.922 #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()923 .map(|data| { data.len() })924 .sum())]925 #[transactional]926 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {927928 ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);929 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);930 let collection = Self::get_collection(collection_id)?;931932 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;933934 Self::submit_logs(collection)?;935 Ok(())936 }937938 /// Destroys a concrete instance of NFT.939 /// 940 /// # Permissions941 /// 942 /// * Collection Owner.943 /// * Collection Admin.944 /// * Current NFT Owner.945 /// 946 /// # Arguments947 /// 948 /// * collection_id: ID of the collection.949 /// 950 /// * item_id: ID of NFT to burn.951 #[weight = <T as Config>::WeightInfo::burn_item()]952 #[transactional]953 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {954955 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);956 let target_collection = Self::get_collection(collection_id)?;957958 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;959960 Self::submit_logs(target_collection)?;961 Ok(())962 }963964 /// Change ownership of the token.965 /// 966 /// # Permissions967 /// 968 /// * Collection Owner969 /// * Collection Admin970 /// * Current NFT owner971 ///972 /// # Arguments973 /// 974 /// * recipient: Address of token recipient.975 /// 976 /// * collection_id.977 /// 978 /// * item_id: ID of the item979 /// * Non-Fungible Mode: Required.980 /// * Fungible Mode: Ignored.981 /// * Re-Fungible Mode: Required.982 /// 983 /// * value: Amount to transfer.984 /// * Non-Fungible Mode: Ignored985 /// * Fungible Mode: Must specify transferred amount986 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)987 #[weight = <T as Config>::WeightInfo::transfer()]988 #[transactional]989 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {990 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);991 let collection = Self::get_collection(collection_id)?;992993 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;994995 Self::submit_logs(collection)?;996 Ok(())997 }998999 /// Set, change, or remove approved address to transfer the ownership of the NFT.1000 /// 1001 /// # Permissions1002 /// 1003 /// * Collection Owner1004 /// * Collection Admin1005 /// * Current NFT owner1006 /// 1007 /// # Arguments1008 /// 1009 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1010 /// 1011 /// * collection_id.1012 /// 1013 /// * item_id: ID of the item.1014 #[weight = <T as Config>::WeightInfo::approve()]1015 #[transactional]1016 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1017 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1018 let collection = Self::get_collection(collection_id)?;10191020 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10211022 Self::submit_logs(collection)?;1023 Ok(())1024 }1025 1026 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1027 /// 1028 /// # Permissions1029 /// * Collection Owner1030 /// * Collection Admin1031 /// * Current NFT owner1032 /// * Address approved by current NFT owner1033 /// 1034 /// # Arguments1035 /// 1036 /// * from: Address that owns token.1037 /// 1038 /// * recipient: Address of token recipient.1039 /// 1040 /// * collection_id.1041 /// 1042 /// * item_id: ID of the item.1043 /// 1044 /// * value: Amount to transfer.1045 #[weight = <T as Config>::WeightInfo::transfer_from()]1046 #[transactional]1047 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1048 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1049 let collection = Self::get_collection(collection_id)?;10501051 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10521053 Self::submit_logs(collection)?;1054 Ok(())1055 }1056 // #[weight = 0]1057 // // let no_perm_mes = "You do not have permissions to modify this collection";1058 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1059 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1060 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10611062 // // // on_nft_received call10631064 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10651066 // Ok(())1067 // }10681069 /// Set off-chain data schema.1070 /// 1071 /// # Permissions1072 /// 1073 /// * Collection Owner1074 /// * Collection Admin1075 /// 1076 /// # Arguments1077 /// 1078 /// * collection_id.1079 /// 1080 /// * schema: String representing the offchain data schema.1081 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1082 #[transactional]1083 pub fn set_variable_meta_data (1084 origin,1085 collection_id: CollectionId,1086 item_id: TokenId,1087 data: Vec<u8>1088 ) -> DispatchResult {1089 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1090 1091 let collection = Self::get_collection(collection_id)?;10921093 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10941095 Ok(())1096 }1097 1098 /// Set schema standard1099 /// ImageURL1100 /// Unique1101 /// 1102 /// # Permissions1103 /// 1104 /// * Collection Owner1105 /// * Collection Admin1106 /// 1107 /// # Arguments1108 /// 1109 /// * collection_id.1110 /// 1111 /// * schema: SchemaVersion: enum1112 #[weight = <T as Config>::WeightInfo::set_schema_version()]1113 #[transactional]1114 pub fn set_schema_version(1115 origin,1116 collection_id: CollectionId,1117 version: SchemaVersion1118 ) -> DispatchResult {1119 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1120 let mut target_collection = Self::get_collection(collection_id)?;1121 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1122 target_collection.schema_version = version;1123 Self::save_collection(target_collection);11241125 Ok(())1126 }11271128 /// Set off-chain data schema.1129 /// 1130 /// # Permissions1131 /// 1132 /// * Collection Owner1133 /// * Collection Admin1134 /// 1135 /// # Arguments1136 /// 1137 /// * collection_id.1138 /// 1139 /// * schema: String representing the offchain data schema.1140 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1141 #[transactional]1142 pub fn set_offchain_schema(1143 origin,1144 collection_id: CollectionId,1145 schema: Vec<u8>1146 ) -> DispatchResult {1147 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1148 let mut target_collection = Self::get_collection(collection_id)?;1149 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11501151 // check schema limit1152 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");11531154 target_collection.offchain_schema = schema;1155 Self::save_collection(target_collection);11561157 Ok(())1158 }11591160 /// Set const on-chain data schema.1161 /// 1162 /// # Permissions1163 /// 1164 /// * Collection Owner1165 /// * Collection Admin1166 /// 1167 /// # Arguments1168 /// 1169 /// * collection_id.1170 /// 1171 /// * schema: String representing the const on-chain data schema.1172 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1173 #[transactional]1174 pub fn set_const_on_chain_schema (1175 origin,1176 collection_id: CollectionId,1177 schema: Vec<u8>1178 ) -> DispatchResult {1179 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1180 let mut target_collection = Self::get_collection(collection_id)?;1181 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11821183 // check schema limit1184 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");11851186 target_collection.const_on_chain_schema = schema;1187 Self::save_collection(target_collection);11881189 Ok(())1190 }11911192 /// Set variable on-chain data schema.1193 /// 1194 /// # Permissions1195 /// 1196 /// * Collection Owner1197 /// * Collection Admin1198 /// 1199 /// # Arguments1200 /// 1201 /// * collection_id.1202 /// 1203 /// * schema: String representing the variable on-chain data schema.1204 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1205 #[transactional]1206 pub fn set_variable_on_chain_schema (1207 origin,1208 collection_id: CollectionId,1209 schema: Vec<u8>1210 ) -> DispatchResult {1211 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1212 let mut target_collection = Self::get_collection(collection_id)?;1213 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12141215 // check schema limit1216 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");12171218 target_collection.variable_on_chain_schema = schema;1219 Self::save_collection(target_collection);12201221 Ok(())1222 }12231224 // Sudo permissions function1225 #[weight = <T as Config>::WeightInfo::set_chain_limits()]1226 #[transactional]1227 pub fn set_chain_limits(1228 origin,1229 limits: ChainLimits1230 ) -> DispatchResult {12311232 #[cfg(not(feature = "runtime-benchmarks"))]1233 ensure_root(origin)?;12341235 <ChainLimit>::put(limits);1236 Ok(())1237 }12381239 /// Enable smart contract self-sponsoring.1240 /// 1241 /// # Permissions1242 /// 1243 /// * Contract Owner1244 /// 1245 /// # Arguments1246 /// 1247 /// * contract address1248 /// * enable flag1249 /// 1250 #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1251 #[transactional]1252 pub fn enable_contract_sponsoring(1253 origin,1254 contract_address: T::AccountId,1255 enable: bool1256 ) -> DispatchResult {12571258 let sender = ensure_signed(origin)?;12591260 #[cfg(feature = "runtime-benchmarks")]1261 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12621263 Self::ensure_contract_owned(sender, &contract_address)?;12641265 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1266 Ok(())1267 }12681269 /// Set the rate limit for contract sponsoring to specified number of blocks.1270 /// 1271 /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1272 /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1273 /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1274 /// from contract endowment if there are at least B blocks between such transactions. 1275 /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1276 /// 1277 /// # Permissions1278 /// 1279 /// * Contract Owner1280 /// 1281 /// # Arguments1282 /// 1283 /// -`contract_address`: Address of the contract to sponsor1284 /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1285 /// 1286 #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1287 #[transactional]1288 pub fn set_contract_sponsoring_rate_limit(1289 origin,1290 contract_address: T::AccountId,1291 rate_limit: T::BlockNumber1292 ) -> DispatchResult {1293 let sender = ensure_signed(origin)?;12941295 #[cfg(feature = "runtime-benchmarks")]1296 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());12971298 Self::ensure_contract_owned(sender, &contract_address)?;1299 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1300 Ok(())1301 }13021303 /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1304 /// 1305 /// # Permissions1306 /// 1307 /// * Address that deployed smart contract.1308 /// 1309 /// # Arguments1310 /// 1311 /// -`contract_address`: Address of the contract.1312 /// 1313 /// - `enable`: . 1314 #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1315 #[transactional]1316 pub fn toggle_contract_white_list(1317 origin,1318 contract_address: T::AccountId,1319 enable: bool1320 ) -> DispatchResult {1321 let sender = ensure_signed(origin)?;13221323 #[cfg(feature = "runtime-benchmarks")]1324 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13251326 Self::ensure_contract_owned(sender, &contract_address)?;1327 if enable {1328 <ContractWhiteListEnabled<T>>::insert(contract_address, true);1329 } else {1330 <ContractWhiteListEnabled<T>>::remove(contract_address);1331 }1332 Ok(())1333 }1334 1335 /// Add an address to smart contract white list.1336 /// 1337 /// # Permissions1338 /// 1339 /// * Address that deployed smart contract.1340 /// 1341 /// # Arguments1342 /// 1343 /// -`contract_address`: Address of the contract.1344 ///1345 /// -`account_address`: Address to add.1346 #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1347 #[transactional]1348 pub fn add_to_contract_white_list(1349 origin,1350 contract_address: T::AccountId,1351 account_address: T::AccountId1352 ) -> DispatchResult {1353 let sender = ensure_signed(origin)?;13541355 #[cfg(feature = "runtime-benchmarks")]1356 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1357 1358 Self::ensure_contract_owned(sender, &contract_address)?; 1359 <ContractWhiteList<T>>::insert(contract_address, account_address, true);1360 Ok(())1361 }13621363 /// Remove an address from smart contract white list.1364 /// 1365 /// # Permissions1366 /// 1367 /// * Address that deployed smart contract.1368 /// 1369 /// # Arguments1370 /// 1371 /// -`contract_address`: Address of the contract.1372 ///1373 /// -`account_address`: Address to remove.1374 #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1375 #[transactional]1376 pub fn remove_from_contract_white_list(1377 origin,1378 contract_address: T::AccountId,1379 account_address: T::AccountId1380 ) -> DispatchResult {1381 let sender = ensure_signed(origin)?;13821383 #[cfg(feature = "runtime-benchmarks")]1384 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());13851386 Self::ensure_contract_owned(sender, &contract_address)?;1387 <ContractWhiteList<T>>::remove(contract_address, account_address);1388 Ok(())1389 }13901391 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1392 #[transactional]1393 pub fn set_collection_limits(1394 origin,1395 collection_id: u32,1396 new_limits: CollectionLimits<T::BlockNumber>,1397 ) -> DispatchResult {1398 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1399 let mut target_collection = Self::get_collection(collection_id)?;1400 Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1401 let old_limits = &target_collection.limits;1402 let chain_limits = ChainLimit::get();14031404 // collection bounds1405 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1406 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1407 new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1408 Error::<T>::CollectionLimitBoundsExceeded);14091410 // token_limit check prev1411 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1412 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);14131414 ensure!(1415 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1416 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1417 Error::<T>::OwnerPermissionsCantBeReverted,1418 );14191420 target_collection.limits = new_limits;1421 Self::save_collection(target_collection);14221423 Ok(())1424 } 1425 }1426}14271428impl<T: Config> Module<T> {1429 pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1430 Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1431 Self::validate_create_item_args(&collection, &data)?;1432 Self::create_item_no_validation(&collection, owner, data)?;14331434 Ok(())1435 }14361437 pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1438 target_collection.consume_gas(2000000)?;1439 // Limits check1440 Self::is_correct_transfer(target_collection, &recipient)?;14411442 // Transfer permissions check1443 ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1444 Self::is_owner_or_admin_permissions(target_collection, &sender),1445 Error::<T>::NoPermission);14461447 if target_collection.access == AccessMode::WhiteList {1448 Self::check_white_list(target_collection, &sender)?;1449 Self::check_white_list(target_collection, &recipient)?;1450 }14511452 match target_collection.mode1453 {1454 CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1455 CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1456 CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1457 _ => ()1458 };14591460 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));14611462 Ok(())1463 }14641465 pub fn approve_internal(1466 sender: &T::CrossAccountId,1467 spender: &T::CrossAccountId,1468 collection: &CollectionHandle<T>,1469 item_id: TokenId,1470 amount: u1281471 ) -> DispatchResult {1472 collection.consume_gas(2000000)?;1473 Self::token_exists(&collection, item_id)?;14741475 // Transfer permissions check1476 let bypasses_limits = collection.limits.owner_can_transfer &&1477 Self::is_owner_or_admin_permissions(1478 &collection,1479 &sender,1480 );14811482 let allowance_limit = if bypasses_limits {1483 None1484 } else if let Some(amount) = Self::owned_amount(1485 &sender,1486 &collection,1487 item_id,1488 ) {1489 Some(amount)1490 } else {1491 fail!(Error::<T>::NoPermission);1492 };14931494 if collection.access == AccessMode::WhiteList {1495 Self::check_white_list(&collection, &sender)?;1496 Self::check_white_list(&collection, &spender)?;1497 }14981499 let allowance: u128 = amount1500 .checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1501 .ok_or(Error::<T>::NumOverflow)?;1502 if let Some(limit) = allowance_limit {1503 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1504 }1505 <Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);15061507 if matches!(collection.mode, CollectionMode::NFT) {1508 // TODO: NFT: only one owner may exist for token in ERC7211509 collection.log(ERC721Events::Approval {1510 owner: *sender.as_eth(),1511 approved: *spender.as_eth(),1512 token_id: item_id.into(),1513 });1514 }15151516 if matches!(collection.mode, CollectionMode::Fungible(_)) {1517 // TODO: NFT: only one owner may exist for token in ERC201518 collection.log(ERC20Events::Approval {1519 owner: *sender.as_eth(),1520 spender: *spender.as_eth(),1521 value: allowance.into()1522 });1523 }15241525 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1526 Ok(())1527 }15281529 pub fn transfer_from_internal(1530 sender: &T::CrossAccountId,1531 from: &T::CrossAccountId,1532 recipient: &T::CrossAccountId,1533 collection: &CollectionHandle<T>,1534 item_id: TokenId,1535 amount: u128,1536 ) -> DispatchResult {1537 collection.consume_gas(2000000)?;1538 // Check approval1539 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));15401541 // Limits check1542 Self::is_correct_transfer(&collection, &recipient)?;15431544 // Transfer permissions check1545 ensure!(1546 approval >= amount || 1547 (1548 collection.limits.owner_can_transfer &&1549 Self::is_owner_or_admin_permissions(&collection, &sender)1550 ),1551 Error::<T>::NoPermission1552 );15531554 if collection.access == AccessMode::WhiteList {1555 Self::check_white_list(&collection, &sender)?;1556 Self::check_white_list(&collection, &recipient)?;1557 }15581559 // Reduce approval by transferred amount or remove if remaining approval drops to 01560 let allowance = approval.saturating_sub(amount);1561 if allowance > 0 {1562 <Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1563 } else {1564 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1565 }15661567 match collection.mode {1568 CollectionMode::NFT => {1569 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1570 }1571 CollectionMode::Fungible(_) => {1572 Self::transfer_fungible(&collection, amount, &from, &recipient)?1573 }1574 CollectionMode::ReFungible => {1575 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1576 }1577 _ => ()1578 };15791580 if matches!(collection.mode, CollectionMode::Fungible(_)) {1581 collection.log(ERC20Events::Approval {1582 owner: *from.as_eth(),1583 spender: *sender.as_eth(),1584 value: allowance.into()1585 });1586 }15871588 Ok(())1589 }15901591 pub fn set_variable_meta_data_internal(1592 sender: &T::CrossAccountId,1593 collection: &CollectionHandle<T>, 1594 item_id: TokenId,1595 data: Vec<u8>,1596 ) -> DispatchResult {1597 Self::token_exists(&collection, item_id)?;15981599 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16001601 // Modify permissions check1602 ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1603 Self::is_owner_or_admin_permissions(&collection, &sender),1604 Error::<T>::NoPermission);16051606 match collection.mode1607 {1608 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1609 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1610 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1611 _ => fail!(Error::<T>::UnexpectedCollectionType)1612 };16131614 Ok(())1615 }16161617 pub fn create_multiple_items_internal(1618 sender: &T::CrossAccountId,1619 collection: &CollectionHandle<T>,1620 owner: &T::CrossAccountId,1621 items_data: Vec<CreateItemData>,1622 ) -> DispatchResult {1623 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;16241625 for data in &items_data {1626 Self::validate_create_item_args(&collection, data)?;1627 }1628 for data in &items_data {1629 Self::create_item_no_validation(&collection, owner, data.clone())?;1630 }16311632 Ok(())1633 }16341635 pub fn burn_item_internal(1636 sender: &T::CrossAccountId,1637 collection: &CollectionHandle<T>,1638 item_id: TokenId,1639 value: u128,1640 ) -> DispatchResult {1641 ensure!(1642 Self::is_item_owner(&sender, &collection, item_id) ||1643 (1644 collection.limits.owner_can_transfer &&1645 Self::is_owner_or_admin_permissions(&collection, &sender)1646 ),1647 Error::<T>::NoPermission1648 );16491650 if collection.access == AccessMode::WhiteList {1651 Self::check_white_list(&collection, &sender)?;1652 }16531654 match collection.mode1655 {1656 CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1657 CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &collection, value)?,1658 CollectionMode::ReFungible => Self::burn_refungible_item(&collection, item_id, &sender)?,1659 _ => ()1660 };16611662 Ok(())1663 }16641665 pub fn toggle_white_list_internal(1666 sender: &T::CrossAccountId,1667 collection: &CollectionHandle<T>,1668 address: &T::CrossAccountId,1669 whitelisted: bool,1670 ) -> DispatchResult {1671 Self::check_owner_or_admin_permissions(&collection, &sender)?;16721673 if whitelisted {1674 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1675 } else {1676 <WhiteList<T>>::remove(collection.id, address.as_sub());1677 }16781679 Ok(())1680 }16811682 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1683 let collection_id = collection.id;16841685 // check token limit and account token limit1686 let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1687 ensure!(collection.limits.account_token_ownership_limit > account_items, Error::<T>::AccountTokenLimitExceeded);1688 1689 Ok(())1690 }16911692 fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1693 let collection_id = collection.id;16941695 // check token limit and account token limit1696 let total_items: u32 = ItemListIndex::get(collection_id)1697 .checked_add(amount)1698 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1699 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1700 .checked_add(amount)1701 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1702 ensure!(collection.limits.token_limit >= total_items, Error::<T>::CollectionTokenLimitExceeded);1703 ensure!(collection.limits.account_token_ownership_limit >= account_items, Error::<T>::AccountTokenLimitExceeded);17041705 if !Self::is_owner_or_admin_permissions(collection, &sender) {1706 ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1707 Self::check_white_list(collection, owner)?;1708 Self::check_white_list(collection, sender)?;1709 }17101711 Ok(())1712 }17131714 fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1715 match target_collection.mode1716 {1717 CollectionMode::NFT => {1718 if let CreateItemData::NFT(data) = data {1719 // check sizes1720 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1721 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1722 } else {1723 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1724 }1725 },1726 CollectionMode::Fungible(_) => {1727 if let CreateItemData::Fungible(_) = data {1728 } else {1729 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1730 }1731 },1732 CollectionMode::ReFungible => {1733 if let CreateItemData::ReFungible(data) = data {17341735 // check sizes1736 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1737 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17381739 // Check refungibility limits1740 ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1741 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1742 } else {1743 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1744 }1745 },1746 _ => { fail!(Error::<T>::UnexpectedCollectionType); }1747 };17481749 Ok(())1750 }17511752 fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1753 match data1754 {1755 CreateItemData::NFT(data) => {1756 let item = NftItemType {1757 owner: owner.clone(),1758 const_data: data.const_data,1759 variable_data: data.variable_data1760 };17611762 Self::add_nft_item(collection, item)?;1763 },1764 CreateItemData::Fungible(data) => {1765 Self::add_fungible_item(collection, &owner, data.value)?;1766 },1767 CreateItemData::ReFungible(data) => {1768 let mut owner_list = Vec::new();1769 owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17701771 let item = ReFungibleItemType {1772 owner: owner_list,1773 const_data: data.const_data,1774 variable_data: data.variable_data1775 };17761777 Self::add_refungible_item(collection, item)?;1778 }1779 };17801781 Ok(())1782 }17831784 fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {1785 let collection_id = collection.id;17861787 // Does new owner already have an account?1788 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17891790 // Mint 1791 let item = FungibleItemType {1792 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1793 };1794 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17951796 // Update balance1797 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1798 .checked_add(value)1799 .ok_or(Error::<T>::NumOverflow)?;1800 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18011802 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1803 Ok(())1804 }18051806 fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {1807 let collection_id = collection.id;18081809 let current_index = <ItemListIndex>::get(collection_id)1810 .checked_add(1)1811 .ok_or(Error::<T>::NumOverflow)?;1812 let itemcopy = item.clone();18131814 ensure!(1815 item.owner.len() == 1,1816 Error::<T>::BadCreateRefungibleCall,1817 );1818 let item_owner = item.owner.first().expect("only one owner is defined");18191820 let value = item_owner.fraction;1821 let owner = item_owner.owner.clone();18221823 Self::add_token_index(collection_id, current_index, &owner)?;18241825 <ItemListIndex>::insert(collection_id, current_index);1826 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18271828 // Update balance1829 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830 .checked_add(value)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1835 Ok(())1836 }18371838 fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {1839 let collection_id = collection.id;18401841 let current_index = <ItemListIndex>::get(collection_id)1842 .checked_add(1)1843 .ok_or(Error::<T>::NumOverflow)?;18441845 let item_owner = item.owner.clone();1846 Self::add_token_index(collection_id, current_index, &item.owner)?;18471848 <ItemListIndex>::insert(collection_id, current_index);1849 <NftItemList<T>>::insert(collection_id, current_index, item);18501851 // Update balance1852 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1853 .checked_add(1)1854 .ok_or(Error::<T>::NumOverflow)?;1855 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);18561857 collection.log(ERC721Events::Transfer {1858 from: H160::default(),1859 to: *item_owner.as_eth(),1860 token_id: current_index.into(),1861 });1862 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1863 Ok(())1864 }18651866 fn burn_refungible_item(1867 collection: &CollectionHandle<T>,1868 item_id: TokenId,1869 owner: &T::CrossAccountId,1870 ) -> DispatchResult {1871 let collection_id = collection.id;18721873 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1874 .ok_or(Error::<T>::TokenNotFound)?;1875 let rft_balance = token1876 .owner1877 .iter()1878 .find(|&i| i.owner == *owner)1879 .ok_or(Error::<T>::TokenNotFound)?;1880 Self::remove_token_index(collection_id, item_id, owner)?;18811882 // update balance1883 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1884 .checked_sub(rft_balance.fraction)1885 .ok_or(Error::<T>::NumOverflow)?;1886 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18871888 // Re-create owners list with sender removed1889 let index = token1890 .owner1891 .iter()1892 .position(|i| i.owner == *owner)1893 .expect("owned item is exists");1894 token.owner.remove(index);1895 let owner_count = token.owner.len();18961897 // Burn the token completely if this was the last (only) owner1898 if owner_count == 0 {1899 <ReFungibleItemList<T>>::remove(collection_id, item_id);1900 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1901 }1902 else {1903 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1904 }19051906 Ok(())1907 }19081909 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1910 let collection_id = collection.id;19111912 let item = <NftItemList<T>>::get(collection_id, item_id)1913 .ok_or(Error::<T>::TokenNotFound)?;1914 Self::remove_token_index(collection_id, item_id, &item.owner)?;19151916 // update balance1917 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1918 .checked_sub(1)1919 .ok_or(Error::<T>::NumOverflow)?;1920 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1921 <NftItemList<T>>::remove(collection_id, item_id);1922 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);19231924 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1925 Ok(())1926 }19271928 fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {1929 let collection_id = collection.id;19301931 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1932 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19331934 // update balance1935 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1936 .checked_sub(value)1937 .ok_or(Error::<T>::NumOverflow)?;1938 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);19391940 if balance.value - value > 0 {1941 balance.value -= value;1942 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1943 }1944 else {1945 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1946 }19471948 collection.log(ERC20Events::Transfer {1949 from: *owner.as_eth(),1950 to: H160::default(),1951 value: value.into(),1952 });1953 Ok(())1954 }19551956 pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1957 Ok(<CollectionHandle<T>>::get(collection_id)1958 .ok_or(Error::<T>::CollectionNotFound)?)1959 }19601961 fn save_collection(collection: CollectionHandle<T>) {1962 <CollectionById<T>>::insert(collection.id, collection.into_inner());1963 }19641965 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1966 if collection.logs.is_empty() {1967 return Ok(())1968 }1969 T::EthereumTransactionSender::submit_logs_transaction(1970 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1971 collection.logs.retrieve_logs(),1972 )1973 }19741975 fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {1976 ensure!(1977 *subject == target_collection.owner,1978 Error::<T>::NoPermission1979 );19801981 Ok(())1982 }19831984 fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {1985 *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)1986 }19871988 fn check_owner_or_admin_permissions(1989 collection: &CollectionHandle<T>,1990 subject: &T::CrossAccountId,1991 ) -> DispatchResult {1992 ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);19931994 Ok(())1995 }19961997 fn owned_amount(1998 subject: &T::CrossAccountId,1999 target_collection: &CollectionHandle<T>,2000 item_id: TokenId,2001 ) -> Option<u128> {2002 let collection_id = target_collection.id;20032004 match target_collection.mode {2005 CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2006 .then(|| 1),2007 CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2008 .value),2009 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2010 .owner2011 .iter()2012 .find(|i| i.owner == *subject)2013 .map(|i| i.fraction),2014 CollectionMode::Invalid => None,2015 }2016 }20172018 fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2019 match target_collection.mode {2020 CollectionMode::Fungible(_) => true,2021 _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2022 }2023 }20242025 fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2026 let collection_id = collection.id;20272028 let mes = Error::<T>::AddresNotInWhiteList;2029 ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);20302031 Ok(())2032 }20332034 /// Check if token exists. In case of Fungible, check if there is an entry for 2035 /// the owner in fungible balances double map2036 fn token_exists(2037 target_collection: &CollectionHandle<T>,2038 item_id: TokenId,2039 ) -> DispatchResult {2040 let collection_id = target_collection.id;2041 let exists = match target_collection.mode2042 {2043 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2044 CollectionMode::Fungible(_) => true,2045 CollectionMode::ReFungible => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2046 _ => false2047 };20482049 ensure!(exists == true, Error::<T>::TokenNotFound);2050 Ok(())2051 }20522053 fn transfer_fungible(2054 collection: &CollectionHandle<T>,2055 value: u128,2056 owner: &T::CrossAccountId,2057 recipient: &T::CrossAccountId,2058 ) -> DispatchResult {2059 let collection_id = collection.id;20602061 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2062 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20632064 // Send balance to recipient (updates balanceOf of recipient)2065 Self::add_fungible_item(collection, recipient, value)?;20662067 // update balanceOf of sender2068 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);20692070 // Reduce or remove sender2071 if balance.value == value {2072 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2073 }2074 else {2075 balance.value -= value;2076 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2077 }20782079 collection.log(ERC20Events::Transfer {2080 from: *owner.as_eth(),2081 to: *recipient.as_eth(),2082 value: value.into(),2083 });2084 Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));20852086 Ok(())2087 }20882089 fn transfer_refungible(2090 collection: &CollectionHandle<T>,2091 item_id: TokenId,2092 value: u128,2093 owner: T::CrossAccountId,2094 new_owner: T::CrossAccountId,2095 ) -> DispatchResult {2096 let collection_id = collection.id;2097 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2098 .ok_or(Error::<T>::TokenNotFound)?;20992100 let item = full_item2101 .owner2102 .iter()2103 .filter(|i| i.owner == owner)2104 .next()2105 .ok_or(Error::<T>::TokenNotFound)?;2106 let amount = item.fraction;21072108 ensure!(amount >= value, Error::<T>::TokenValueTooLow);21092110 // update balance2111 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2112 .checked_sub(value)2113 .ok_or(Error::<T>::NumOverflow)?;2114 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21152116 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2117 .checked_add(value)2118 .ok_or(Error::<T>::NumOverflow)?;2119 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21202121 let old_owner = item.owner.clone();2122 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21232124 // transfer2125 if amount == value && !new_owner_has_account {2126 // change owner2127 // new owner do not have account2128 let mut new_full_item = full_item.clone();2129 new_full_item2130 .owner2131 .iter_mut()2132 .find(|i| i.owner == owner)2133 .expect("old owner does present in refungible")2134 .owner = new_owner.clone();2135 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21362137 // update index collection2138 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2139 } else {2140 let mut new_full_item = full_item.clone();2141 new_full_item2142 .owner2143 .iter_mut()2144 .find(|i| i.owner == owner)2145 .expect("old owner does present in refungible")2146 .fraction -= value;21472148 // separate amount2149 if new_owner_has_account {2150 // new owner has account2151 new_full_item2152 .owner2153 .iter_mut()2154 .find(|i| i.owner == new_owner)2155 .expect("new owner has account")2156 .fraction += value;2157 } else {2158 // new owner do not have account2159 new_full_item.owner.push(Ownership {2160 owner: new_owner.clone(),2161 fraction: value,2162 });2163 Self::add_token_index(collection_id, item_id, &new_owner)?;2164 }21652166 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2167 }21682169 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));21702171 Ok(())2172 }21732174 fn transfer_nft(2175 collection: &CollectionHandle<T>,2176 item_id: TokenId,2177 sender: T::CrossAccountId,2178 new_owner: T::CrossAccountId,2179 ) -> DispatchResult {2180 let collection_id = collection.id;2181 let mut item = <NftItemList<T>>::get(collection_id, item_id)2182 .ok_or(Error::<T>::TokenNotFound)?;21832184 ensure!(2185 sender == item.owner,2186 Error::<T>::MustBeTokenOwner2187 );21882189 // update balance2190 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2191 .checked_sub(1)2192 .ok_or(Error::<T>::NumOverflow)?;2193 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21942195 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2196 .checked_add(1)2197 .ok_or(Error::<T>::NumOverflow)?;2198 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21992200 // change owner2201 let old_owner = item.owner.clone();2202 item.owner = new_owner.clone();2203 <NftItemList<T>>::insert(collection_id, item_id, item);22042205 // update index collection2206 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22072208 collection.log(ERC721Events::Transfer {2209 from: *sender.as_eth(),2210 to: *new_owner.as_eth(),2211 token_id: item_id.into(),2212 });2213 Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));22142215 Ok(())2216 }2217 2218 fn set_re_fungible_variable_data(2219 collection: &CollectionHandle<T>,2220 item_id: TokenId,2221 data: Vec<u8>2222 ) -> DispatchResult {2223 let collection_id = collection.id;2224 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2225 .ok_or(Error::<T>::TokenNotFound)?;22262227 item.variable_data = data;22282229 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22302231 Ok(())2232 }22332234 fn set_nft_variable_data(2235 collection: &CollectionHandle<T>,2236 item_id: TokenId,2237 data: Vec<u8>2238 ) -> DispatchResult {2239 let collection_id = collection.id;2240 let mut item = <NftItemList<T>>::get(collection_id, item_id)2241 .ok_or(Error::<T>::TokenNotFound)?;2242 2243 item.variable_data = data;22442245 <NftItemList<T>>::insert(collection_id, item_id, item);2246 2247 Ok(())2248 }22492250 #[allow(dead_code)]2251 fn init_collection(item: &Collection<T>) {2252 // check params2253 assert!(2254 item.decimal_points <= MAX_DECIMAL_POINTS,2255 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2256 );2257 assert!(2258 item.name.len() <= 64,2259 "Collection name can not be longer than 63 char"2260 );2261 assert!(2262 item.name.len() <= 256,2263 "Collection description can not be longer than 255 char"2264 );2265 assert!(2266 item.token_prefix.len() <= 16,2267 "Token prefix can not be longer than 15 char"2268 );22692270 // Generate next collection ID2271 let next_id = CreatedCollectionCount::get()2272 .checked_add(1)2273 .unwrap();22742275 CreatedCollectionCount::put(next_id);2276 }22772278 #[allow(dead_code)]2279 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2280 let current_index = <ItemListIndex>::get(collection_id)2281 .checked_add(1)2282 .unwrap();22832284 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22852286 <ItemListIndex>::insert(collection_id, current_index);22872288 // Update balance2289 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2290 .checked_add(1)2291 .unwrap();2292 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2293 }22942295 #[allow(dead_code)]2296 fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2297 let current_index = <ItemListIndex>::get(collection_id)2298 .checked_add(1)2299 .unwrap();23002301 Self::add_token_index(collection_id, current_index, owner).unwrap();23022303 <ItemListIndex>::insert(collection_id, current_index);23042305 // Update balance2306 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2307 .checked_add(item.value)2308 .unwrap();2309 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2310 }23112312 #[allow(dead_code)]2313 fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2314 let current_index = <ItemListIndex>::get(collection_id)2315 .checked_add(1)2316 .unwrap();23172318 let value = item.owner.first().unwrap().fraction;2319 let owner = item.owner.first().unwrap().owner.clone();23202321 Self::add_token_index(collection_id, current_index, &owner).unwrap();23222323 <ItemListIndex>::insert(collection_id, current_index);23242325 // Update balance2326 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2327 .checked_add(value)2328 .unwrap();2329 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2330 }23312332 fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2333 // add to account limit2334 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {23352336 // bound Owned tokens by a single address2337 let count = <AccountItemCount<T>>::get(owner.as_sub());2338 ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23392340 <AccountItemCount<T>>::insert(owner.as_sub(), count2341 .checked_add(1)2342 .ok_or(Error::<T>::NumOverflow)?);2343 }2344 else {2345 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2346 }23472348 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2349 if list_exists {2350 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2351 let item_contains = list.contains(&item_index.clone());23522353 if !item_contains {2354 list.push(item_index.clone());2355 }23562357 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2358 } else {2359 let mut itm = Vec::new();2360 itm.push(item_index.clone());2361 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2362 }23632364 Ok(())2365 }23662367 fn remove_token_index(2368 collection_id: CollectionId,2369 item_index: TokenId,2370 owner: &T::CrossAccountId,2371 ) -> DispatchResult {23722373 // update counter2374 <AccountItemCount<T>>::insert(owner.as_sub(), 2375 <AccountItemCount<T>>::get(owner.as_sub())2376 .checked_sub(1)2377 .ok_or(Error::<T>::NumOverflow)?);237823792380 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2381 if list_exists {2382 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2383 let item_contains = list.contains(&item_index.clone());23842385 if item_contains {2386 list.retain(|&item| item != item_index);2387 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2388 }2389 }23902391 Ok(())2392 }23932394 fn move_token_index(2395 collection_id: CollectionId,2396 item_index: TokenId,2397 old_owner: &T::CrossAccountId,2398 new_owner: &T::CrossAccountId,2399 ) -> DispatchResult {2400 Self::remove_token_index(collection_id, item_index, old_owner)?;2401 Self::add_token_index(collection_id, item_index, new_owner)?;24022403 Ok(())2404 }2405 2406 fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2407 ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);24082409 Ok(())2410 }2411}24122413sp_api::decl_runtime_apis! {2414 pub trait NftApi {2415 /// Used for ethereum integration2416 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2417 }2418}pallets/nft/src/sponsorship.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/sponsorship.rs
@@ -0,0 +1,203 @@
+use crate::{Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket, ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, ChainLimit, CreateItemData, CollectionMode};
+use core::marker::PhantomData;
+use up_sponsorship::SponsorshipHandler;
+use frame_support::{
+ traits::IsSubType,
+ storage::{StorageMap, StorageDoubleMap, StorageValue},
+};
+use nft_data_structs::{TokenId, CollectionId};
+use alloc::vec::Vec;
+
+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)?;
+
+ // sponsor timeout
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+
+ let limit = collection.limits.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;
+ }
+ }
+ CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);
+
+ // check free create limit
+ if collection.limits.sponsored_data_size >= (_properties.len() as u32) {
+ collection.sponsorship.sponsor()
+ .cloned()
+ } else {
+ None
+ }
+ }
+
+ pub fn withdraw_transfer(
+ who: &T::AccountId,
+ collection_id: &CollectionId,
+ item_id: &TokenId,
+ ) -> Option<T::AccountId> {
+
+ let collection = CollectionById::<T>::get(collection_id)?;
+ let limits = ChainLimit::get();
+
+ let mut sponsor_transfer = false;
+ if collection.sponsorship.confirmed() {
+
+ let collection_limits = collection.limits.clone();
+ let collection_mode = collection.mode.clone();
+
+ // 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: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.nft_sponsor_transfer_timeout
+ };
+
+ 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);
+ }
+
+ sponsored
+ }
+ CollectionMode::Fungible(_) => {
+
+ // get correct limit
+ let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.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) {
+ 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);
+ }
+
+ sponsored
+ }
+ CollectionMode::ReFungible => {
+
+ // get correct limit
+ let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
+ collection_limits.sponsor_transfer_timeout
+ } else {
+ limits.refungible_sponsor_transfer_timeout
+ };
+
+ 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);
+ }
+
+ sponsored
+ }
+ _ => {
+ false
+ },
+ };
+ }
+
+ if !sponsor_transfer {
+ None
+ } else {
+ collection.sponsorship.sponsor()
+ .cloned()
+ }
+ }
+
+ pub fn withdraw_set_variable_meta_data(
+ collection_id: &CollectionId,
+ item_id: &TokenId,
+ data: &Vec<u8>,
+ ) -> Option<T::AccountId> {
+
+ let mut sponsor_metadata_changes = false;
+
+ let collection = CollectionById::<T>::get(collection_id)?;
+
+ 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;
+
+ if VariableMetaDataBasket::<T>::get(collection_id, item_id)
+ .map(|last_block| block_number - last_block > rate_limit)
+ .unwrap_or(true)
+ {
+ sponsor_metadata_changes = true;
+ VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);
+ }
+ }
+ }
+
+ if !sponsor_metadata_changes {
+ None
+ } else {
+ collection.sponsorship.sponsor().cloned()
+ }
+
+ }
+}
+
+impl<T, C> SponsorshipHandler<T::AccountId, C> for NftSponsorshipHandler<T>
+where
+ T: Config,
+ C: IsSubType<Call<T>>
+{
+ fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
+ match IsSubType::<Call<T>>::is_sub_type(call)? {
+ Call::create_item(collection_id, _owner, _properties) => {
+ Self::withdraw_create_item(who, collection_id, &_properties)
+ },
+ Call::transfer(_new_owner, collection_id, item_id, _value) => {
+ Self::withdraw_transfer(who, collection_id, item_id)
+ },
+ Call::set_variable_meta_data(collection_id, item_id, data) => {
+ Self::withdraw_set_variable_meta_data(collection_id, item_id, &data)
+ },
+ _ => None,
+ }
+ }
+}
\ No newline at end of file
pallets/scheduler/Cargo.tomldiffbeforeafterboth--- a/pallets/scheduler/Cargo.toml
+++ b/pallets/scheduler/Cargo.toml
@@ -14,15 +14,12 @@
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
frame-benchmarking = { default-features = false, version = '3.0.0', optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-nft-transaction-payment = { default-features = false, path = "../nft-transaction-payment" }
-pallet-nft = { default-features = false, path = "../nft" }
-nft-data-structs = { path = '../../primitives', default-features = false }
+up-sponsorship = { default-features = false, path = "../../primitives/sponsorship", version = "0.1.0" }
log = { version = "0.4.14", default-features = false }
[dev-dependencies]
@@ -37,10 +34,7 @@
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
- "pallet-nft-transaction-payment/std",
- "pallet-nft/std",
- "pallet-contracts/std",
- "nft-data-structs/std",
+ "up-sponsorship/std",
"sp-io/std",
"sp-std/std",
"log/std",
pallets/scheduler/src/lib.rsdiffbeforeafterboth--- a/pallets/scheduler/src/lib.rs
+++ b/pallets/scheduler/src/lib.rs
@@ -64,10 +64,8 @@
weights::{GetDispatchInfo, Weight},
};
use frame_system::{self as system, ensure_signed};
-use pallet_nft::*;
-// use pallet_nft_transaction_payment::{self as nft_transaction_payment};
-use nft_data_structs::*;
pub use weights::WeightInfo;
+use up_sponsorship::SponsorshipHandler;
/// Our pallet's configuration trait. All our types and constants go in here. If the
/// pallet is dependent on specific other pallets, then their configuration traits
@@ -103,7 +101,7 @@
type MaxScheduledPerBlock: Get<u32>;
/// Sponsoring function
- type Sponsoring: SponsoringResolve<Self::AccountId, <Self as Config>::Call>;
+ type SponsorshipHandler: SponsorshipHandler<Self::AccountId, <Self as Config>::Call>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
@@ -405,8 +403,7 @@
s.origin.clone()
).into();
let sender = ensure_signed(origin).unwrap_or(T::AccountId::default());
- let who_will_pay = T::Sponsoring::resolve(&sender, &s.call.clone()).unwrap_or(
- sender);
+ let who_will_pay = T::SponsorshipHandler::get_sponsor(&sender, &s.call).unwrap_or(sender);
let sponsor = T::PalletsOrigin::from(system::RawOrigin::Signed(who_will_pay));
let r = s.call.clone().dispatch(sponsor.into());
let maybe_id = s.maybe_id.clone();
primitives/Cargo.tomldiffbeforeafterboth--- a/primitives/Cargo.toml
+++ /dev/null
@@ -1,30 +0,0 @@
-[package]
-name = "nft-data-structs"
-authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
-description = "Nft data structs definitions"
-edition = "2018"
-license = 'GPL-3.0'
-homepage = "https://substrate.dev"
-repository = 'https://github.com/clover-network/clover'
-version = '0.9.0'
-
-[dependencies]
-codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ['derive'] }
-serde = { version = "1.0.119", features = ['derive'], default-features = false }
-frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-core = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
-
-[features]
-default = ["std"]
-std = [
- "serde/std",
- "codec/std",
- "frame-system/std",
- "frame-support/std",
- "sp-runtime/std",
- "sp-core/std",
- "pallet-contracts/std",
-]
\ No newline at end of file
primitives/nft/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/primitives/nft/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "nft-data-structs"
+authors = ['Substrate DevHub <https://github.com/substrate-developer-hub>']
+description = "Nft data structs definitions"
+edition = "2018"
+license = 'GPL-3.0'
+homepage = "https://substrate.dev"
+repository = 'https://github.com/clover-network/clover'
+version = '0.9.0'
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ['derive'] }
+serde = { version = "1.0.119", features = ['derive'], default-features = false }
+frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+pallet-contracts = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+sp-core = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+sp-runtime = { version = "3.0.0", default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.3' }
+
+[features]
+default = ["std"]
+std = [
+ "serde/std",
+ "codec/std",
+ "frame-system/std",
+ "frame-support/std",
+ "sp-runtime/std",
+ "sp-core/std",
+ "pallet-contracts/std",
+]
\ No newline at end of file
primitives/nft/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/primitives/nft/src/lib.rs
@@ -0,0 +1,285 @@
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use serde::{Serialize, Deserialize};
+
+use frame_system;
+use sp_runtime::sp_std::prelude::Vec;
+use codec::{Decode, Encode};
+pub use frame_support::{
+ construct_runtime, decl_event, decl_module, decl_storage, decl_error,
+ dispatch::DispatchResult,
+ ensure, fail, parameter_types,
+ traits::{
+ Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,
+ Randomness, IsSubType, WithdrawReasons,
+ },
+ weights::{
+ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
+ DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,
+ WeightToFeePolynomial, DispatchClass,
+ },
+ StorageValue,
+ transactional,
+};
+
+pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;
+pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;
+pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;
+pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;
+
+pub type CollectionId = u32;
+pub type TokenId = u32;
+pub type DecimalPoints = u8;
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum CollectionMode {
+ Invalid,
+ NFT,
+ // decimal points
+ Fungible(DecimalPoints),
+ ReFungible,
+}
+
+impl Default for CollectionMode {
+ fn default() -> Self {
+ Self::Invalid
+ }
+}
+
+impl Into<u8> for CollectionMode {
+ fn into(self) -> u8 {
+ match self {
+ CollectionMode::Invalid => 0,
+ CollectionMode::NFT => 1,
+ CollectionMode::Fungible(_) => 2,
+ CollectionMode::ReFungible => 3,
+ }
+ }
+}
+
+pub trait SponsoringResolve<AccountId, Call> {
+ fn resolve(
+ who: &AccountId,
+ call: &Call) -> Option<AccountId>;
+}
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum AccessMode {
+ Normal,
+ WhiteList,
+}
+impl Default for AccessMode {
+ fn default() -> Self {
+ Self::Normal
+ }
+}
+
+#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum SchemaVersion {
+ ImageURL,
+ Unique,
+}
+impl Default for SchemaVersion {
+ fn default() -> Self {
+ Self::ImageURL
+ }
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct Ownership<AccountId> {
+ pub owner: AccountId,
+ pub fraction: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum SponsorshipState<AccountId> {
+ /// The fees are applied to the transaction sender
+ Disabled,
+ Unconfirmed(AccountId),
+ /// Transactions are sponsored by specified account
+ Confirmed(AccountId),
+}
+
+impl<AccountId> SponsorshipState<AccountId> {
+ pub fn sponsor(&self) -> Option<&AccountId> {
+ match self {
+ Self::Confirmed(sponsor) => Some(sponsor),
+ _ => None,
+ }
+ }
+
+ pub fn pending_sponsor(&self) -> Option<&AccountId> {
+ match self {
+ Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
+ _ => None,
+ }
+ }
+
+ pub fn confirmed(&self) -> bool {
+ matches!(self, Self::Confirmed(_))
+ }
+}
+
+impl<T> Default for SponsorshipState<T> {
+ fn default() -> Self {
+ Self::Disabled
+ }
+}
+
+#[derive(Encode, Decode, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct Collection<T: frame_system::Config> {
+ pub owner: T::AccountId,
+ pub mode: CollectionMode,
+ pub access: AccessMode,
+ pub decimal_points: DecimalPoints,
+ pub name: Vec<u16>, // 64 include null escape char
+ pub description: Vec<u16>, // 256 include null escape char
+ pub token_prefix: Vec<u8>, // 16 include null escape char
+ pub mint_mode: bool,
+ pub offchain_schema: Vec<u8>,
+ pub schema_version: SchemaVersion,
+ pub sponsorship: SponsorshipState<T::AccountId>,
+ pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
+ pub variable_on_chain_schema: Vec<u8>, //
+ pub const_on_chain_schema: Vec<u8>, //
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct NftItemType<AccountId> {
+ pub owner: AccountId,
+ pub const_data: Vec<u8>,
+ pub variable_data: Vec<u8>,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct FungibleItemType {
+ pub value: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ReFungibleItemType<AccountId> {
+ pub owner: Vec<Ownership<AccountId>>,
+ pub const_data: Vec<u8>,
+ pub variable_data: Vec<u8>,
+}
+
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CollectionLimits<BlockNumber: Encode + Decode> {
+ pub account_token_ownership_limit: u32,
+ pub sponsored_data_size: u32,
+ /// None - setVariableMetadata is not sponsored
+ /// Some(v) - setVariableMetadata is sponsored
+ /// if there is v block between txs
+ pub sponsored_data_rate_limit: Option<BlockNumber>,
+ pub token_limit: u32,
+
+ // Timeouts for item types in passed blocks
+ pub sponsor_transfer_timeout: u32,
+ pub owner_can_transfer: bool,
+ pub owner_can_destroy: bool,
+}
+
+impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {
+ fn default() -> Self {
+ Self {
+ account_token_ownership_limit: 10_000_000,
+ token_limit: u32::max_value(),
+ sponsored_data_size: u32::MAX,
+ sponsored_data_rate_limit: None,
+ sponsor_transfer_timeout: 14400,
+ owner_can_transfer: true,
+ owner_can_destroy: true
+ }
+ }
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct ChainLimits {
+ pub collection_numbers_limit: u32,
+ pub account_token_ownership_limit: u32,
+ pub collections_admins_limit: u64,
+ pub custom_data_limit: u32,
+
+ // Timeouts for item types in passed blocks
+ pub nft_sponsor_transfer_timeout: u32,
+ pub fungible_sponsor_transfer_timeout: u32,
+ pub refungible_sponsor_transfer_timeout: u32,
+
+ // Schema limits
+ pub offchain_schema_limit: u32,
+ pub variable_on_chain_schema_limit: u32,
+ pub const_on_chain_schema_limit: u32,
+}
+
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateNftData {
+ pub const_data: Vec<u8>,
+ pub variable_data: Vec<u8>,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateFungibleData {
+ pub value: u128,
+}
+
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub struct CreateReFungibleData {
+ pub const_data: Vec<u8>,
+ pub variable_data: Vec<u8>,
+ pub pieces: u128,
+}
+
+#[derive(Encode, Decode, Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+pub enum CreateItemData {
+ NFT(CreateNftData),
+ Fungible(CreateFungibleData),
+ ReFungible(CreateReFungibleData),
+}
+
+impl CreateItemData {
+ pub fn len(&self) -> usize {
+ let len = match self {
+ CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),
+ CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),
+ _ => 0
+ };
+
+ return len;
+ }
+}
+
+impl From<CreateNftData> for CreateItemData {
+ fn from(item: CreateNftData) -> Self {
+ CreateItemData::NFT(item)
+ }
+}
+
+impl From<CreateReFungibleData> for CreateItemData {
+ fn from(item: CreateReFungibleData) -> Self {
+ CreateItemData::ReFungible(item)
+ }
+}
+
+impl From<CreateFungibleData> for CreateItemData {
+ fn from(item: CreateFungibleData) -> Self {
+ CreateItemData::Fungible(item)
+ }
+}
primitives/sponsorship/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/primitives/sponsorship/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "up-sponsorship"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+impl-trait-for-tuples = "0.2.1"
+
+[features]
+default = ["std"]
+std = []
\ No newline at end of file
primitives/sponsorship/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/primitives/sponsorship/src/lib.rs
@@ -0,0 +1,42 @@
+#![no_std]
+
+pub trait SponsorshipHandler<AccountId, Call> {
+ fn get_sponsor(who: &AccountId, call: &Call) -> Option<AccountId>;
+}
+
+impl<A, C> SponsorshipHandler<A, C> for () {
+ fn get_sponsor(_who: &A, _call: &C) -> Option<A> {
+ None
+ }
+}
+
+macro_rules! impl_tuples {
+ ($($ident:ident)+) => {
+ impl<AccountId, Call, $($ident),+> SponsorshipHandler<AccountId, Call> for ($($ident,)+)
+ where
+ $(
+ $ident: SponsorshipHandler<AccountId, Call>
+ ),+
+ {
+ fn get_sponsor(who: &AccountId, call: &Call) -> Option<AccountId> {
+ $(
+ if let Some(account) = $ident::get_sponsor(who, call) {
+ return Some(account);
+ }
+ )+
+ None
+ }
+ }
+ }
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
primitives/src/lib.rsdiffbeforeafterboth--- a/primitives/src/lib.rs
+++ /dev/null
@@ -1,285 +0,0 @@
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-pub use serde::{Serialize, Deserialize};
-
-use frame_system;
-use sp_runtime::sp_std::prelude::Vec;
-use codec::{Decode, Encode};
-pub use frame_support::{
- construct_runtime, decl_event, decl_module, decl_storage, decl_error,
- dispatch::DispatchResult,
- ensure, fail, parameter_types,
- traits::{
- Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,
- Randomness, IsSubType, WithdrawReasons,
- },
- weights::{
- constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
- DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,
- WeightToFeePolynomial, DispatchClass,
- },
- StorageValue,
- transactional,
-};
-
-pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;
-pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;
-pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;
-pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;
-
-pub type CollectionId = u32;
-pub type TokenId = u32;
-pub type DecimalPoints = u8;
-
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub enum CollectionMode {
- Invalid,
- NFT,
- // decimal points
- Fungible(DecimalPoints),
- ReFungible,
-}
-
-impl Default for CollectionMode {
- fn default() -> Self {
- Self::Invalid
- }
-}
-
-impl Into<u8> for CollectionMode {
- fn into(self) -> u8 {
- match self {
- CollectionMode::Invalid => 0,
- CollectionMode::NFT => 1,
- CollectionMode::Fungible(_) => 2,
- CollectionMode::ReFungible => 3,
- }
- }
-}
-
-pub trait SponsoringResolve<AccountId, Call> {
- fn resolve(
- who: &AccountId,
- call: &Call) -> Option<AccountId>;
-}
-
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub enum AccessMode {
- Normal,
- WhiteList,
-}
-impl Default for AccessMode {
- fn default() -> Self {
- Self::Normal
- }
-}
-
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub enum SchemaVersion {
- ImageURL,
- Unique,
-}
-impl Default for SchemaVersion {
- fn default() -> Self {
- Self::ImageURL
- }
-}
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct Ownership<AccountId> {
- pub owner: AccountId,
- pub fraction: u128,
-}
-
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub enum SponsorshipState<AccountId> {
- /// The fees are applied to the transaction sender
- Disabled,
- Unconfirmed(AccountId),
- /// Transactions are sponsored by specified account
- Confirmed(AccountId),
-}
-
-impl<AccountId> SponsorshipState<AccountId> {
- pub fn sponsor(&self) -> Option<&AccountId> {
- match self {
- Self::Confirmed(sponsor) => Some(sponsor),
- _ => None,
- }
- }
-
- pub fn pending_sponsor(&self) -> Option<&AccountId> {
- match self {
- Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),
- _ => None,
- }
- }
-
- pub fn confirmed(&self) -> bool {
- matches!(self, Self::Confirmed(_))
- }
-}
-
-impl<T> Default for SponsorshipState<T> {
- fn default() -> Self {
- Self::Disabled
- }
-}
-
-#[derive(Encode, Decode, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct Collection<T: frame_system::Config> {
- pub owner: T::AccountId,
- pub mode: CollectionMode,
- pub access: AccessMode,
- pub decimal_points: DecimalPoints,
- pub name: Vec<u16>, // 64 include null escape char
- pub description: Vec<u16>, // 256 include null escape char
- pub token_prefix: Vec<u8>, // 16 include null escape char
- pub mint_mode: bool,
- pub offchain_schema: Vec<u8>,
- pub schema_version: SchemaVersion,
- pub sponsorship: SponsorshipState<T::AccountId>,
- pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions
- pub variable_on_chain_schema: Vec<u8>, //
- pub const_on_chain_schema: Vec<u8>, //
-}
-
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct NftItemType<AccountId> {
- pub owner: AccountId,
- pub const_data: Vec<u8>,
- pub variable_data: Vec<u8>,
-}
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct FungibleItemType {
- pub value: u128,
-}
-
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct ReFungibleItemType<AccountId> {
- pub owner: Vec<Ownership<AccountId>>,
- pub const_data: Vec<u8>,
- pub variable_data: Vec<u8>,
-}
-
-
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct CollectionLimits<BlockNumber: Encode + Decode> {
- pub account_token_ownership_limit: u32,
- pub sponsored_data_size: u32,
- /// None - setVariableMetadata is not sponsored
- /// Some(v) - setVariableMetadata is sponsored
- /// if there is v block between txs
- pub sponsored_data_rate_limit: Option<BlockNumber>,
- pub token_limit: u32,
-
- // Timeouts for item types in passed blocks
- pub sponsor_transfer_timeout: u32,
- pub owner_can_transfer: bool,
- pub owner_can_destroy: bool,
-}
-
-impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {
- fn default() -> Self {
- Self {
- account_token_ownership_limit: 10_000_000,
- token_limit: u32::max_value(),
- sponsored_data_size: u32::MAX,
- sponsored_data_rate_limit: None,
- sponsor_transfer_timeout: 14400,
- owner_can_transfer: true,
- owner_can_destroy: true
- }
- }
-}
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct ChainLimits {
- pub collection_numbers_limit: u32,
- pub account_token_ownership_limit: u32,
- pub collections_admins_limit: u64,
- pub custom_data_limit: u32,
-
- // Timeouts for item types in passed blocks
- pub nft_sponsor_transfer_timeout: u32,
- pub fungible_sponsor_transfer_timeout: u32,
- pub refungible_sponsor_transfer_timeout: u32,
-
- // Schema limits
- pub offchain_schema_limit: u32,
- pub variable_on_chain_schema_limit: u32,
- pub const_on_chain_schema_limit: u32,
-}
-
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct CreateNftData {
- pub const_data: Vec<u8>,
- pub variable_data: Vec<u8>,
-}
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct CreateFungibleData {
- pub value: u128,
-}
-
-#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub struct CreateReFungibleData {
- pub const_data: Vec<u8>,
- pub variable_data: Vec<u8>,
- pub pieces: u128,
-}
-
-#[derive(Encode, Decode, Debug, Clone, PartialEq)]
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-pub enum CreateItemData {
- NFT(CreateNftData),
- Fungible(CreateFungibleData),
- ReFungible(CreateReFungibleData),
-}
-
-impl CreateItemData {
- pub fn len(&self) -> usize {
- let len = match self {
- CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),
- CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),
- _ => 0
- };
-
- return len;
- }
-}
-
-impl From<CreateNftData> for CreateItemData {
- fn from(item: CreateNftData) -> Self {
- CreateItemData::NFT(item)
- }
-}
-
-impl From<CreateReFungibleData> for CreateItemData {
- fn from(item: CreateReFungibleData) -> Self {
- CreateItemData::ReFungible(item)
- }
-}
-
-impl From<CreateFungibleData> for CreateItemData {
- fn from(item: CreateFungibleData) -> Self {
- CreateItemData::Fungible(item)
- }
-}
\ No newline at end of file
runtime/Cargo.tomldiffbeforeafterboth--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -46,6 +46,7 @@
'pallet-contracts/std',
'pallet-contracts-primitives/std',
'pallet-contracts-rpc-runtime-api/std',
+ 'pallet-contract-helpers/std',
'pallet-randomness-collective-flip/std',
'pallet-sudo/std',
'pallet-timestamp/std',
@@ -372,8 +373,9 @@
[dependencies]
pallet-nft = { path = '../pallets/nft', default-features = false, version = '3.0.0' }
pallet-inflation = { path = '../pallets/inflation', default-features = false, version = '3.0.0' }
-nft-data-structs = { path = '../primitives', default-features = false, version = '0.9.0' }
+nft-data-structs = { path = '../primitives/nft', default-features = false, version = '0.9.0' }
pallet-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 = { path = '../pallets/nft-charge-transaction', default-features = false, version = '3.0.0' }
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -734,6 +734,11 @@
}
}
+type SponsorshipHandler = (
+ pallet_nft::NftSponsorshipHandler<Runtime>,
+ pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
+);
+
impl pallet_scheduler::Config for Runtime {
type Event = Event;
type Origin = Origin;
@@ -742,16 +747,18 @@
type MaximumWeight = MaximumSchedulerWeight;
type ScheduleOrigin = EnsureSigned<AccountId>;
type MaxScheduledPerBlock = MaxScheduledPerBlock;
- type Sponsoring = Sponsoring;
+ type SponsorshipHandler = SponsorshipHandler;
type WeightInfo = ();
}
impl pallet_nft_transaction_payment::Config for Runtime {
+ type SponsorshipHandler = SponsorshipHandler;
}
-impl pallet_nft_charge_transaction::Config for Runtime {
-}
+impl pallet_nft_charge_transaction::Config for Runtime {}
+impl pallet_contract_helpers::Config for Runtime {}
+
construct_runtime!(
pub enum Runtime where
Block = Block,
@@ -791,6 +798,7 @@
Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>},
NftPayment: pallet_nft_transaction_payment::{Pallet, Call, Storage},
Charging: pallet_nft_charge_transaction::{Pallet, Call, Storage },
+ ContractHelpers: pallet_contract_helpers::{Pallet, Call, Storage},
}
);
@@ -829,6 +837,7 @@
system::CheckNonce<Runtime>,
system::CheckWeight<Runtime>,
pallet_nft_charge_transaction::ChargeTransactionPayment<Runtime>,
+ pallet_contract_helpers::ContractHelpersExtension<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;