From 3950758e00c36ea8ba7e70ccb74e9fc69eda9d3d Mon Sep 17 00:00:00 2001 From: sotmorskiy Date: Wed, 11 Nov 2020 04:11:17 +0000 Subject: [PATCH] Merge branch 'develop' into feature/NFTPAR-142 # Conflicts: # pallets/nft/src/default_weights.rs # pallets/nft/src/lib.rs --- --- a/Cargo.lock +++ b/Cargo.lock @@ -3737,6 +3737,7 @@ "frame-support", "frame-system", "log", + "pallet-contracts", "pallet-transaction-payment", "parity-scale-codec", "serde", --- a/pallets/nft/Cargo.toml +++ b/pallets/nft/Cargo.toml @@ -73,6 +73,34 @@ branch = 'v2.0.0_release' optional = true +[dependencies.pallet-contracts] +default-features = false +git = 'https://github.com/usetech-llc/substrate.git' +package = 'pallet-contracts' +branch = 'v2.0.0_release' +version = '2.0.0' + +[dependencies.pallet-balances] +default-features = false +git = 'https://github.com/usetech-llc/substrate.git' +package = 'pallet-balances' +branch = 'v2.0.0_release' +version = '2.0.0' + +[dependencies.pallet-timestamp] +default-features = false +git = 'https://github.com/usetech-llc/substrate.git' +package = 'pallet-timestamp' +branch = 'v2.0.0_release' +version = '2.0.0' + +[dependencies.pallet-randomness-collective-flip] +default-features = false +git = 'https://github.com/usetech-llc/substrate.git' +package = 'pallet-randomness-collective-flip' +branch = 'v2.0.0_release' +version = '2.0.0' + [features] default = ['std'] std = [ @@ -80,6 +108,9 @@ "serde/std", 'frame-support/std', 'frame-system/std', + 'pallet-balances/std', + 'pallet-timestamp/std', + 'pallet-randomness-collective-flip/std', 'sp-std/std', 'sp-runtime/std', 'frame-benchmarking/std', --- a/pallets/nft/src/default_weights.rs +++ b/pallets/nft/src/default_weights.rs @@ -107,4 +107,9 @@ .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } + // fn enable_contract_sponsoring() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } } --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -28,15 +28,15 @@ use sp_runtime::sp_std::prelude::Vec; use sp_runtime::{ traits::{ - DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating, - SignedExtension, Zero, + DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero, }, transaction_validity::{ - InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, - ValidTransaction, + InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, }, FixedPointOperand, FixedU128, }; +use pallet_contracts::ContractAddressFor; +use sp_runtime::traits::StaticLookup; #[cfg(test)] mod mock; @@ -208,6 +208,7 @@ fn set_const_on_chain_schema() -> Weight; fn set_variable_on_chain_schema() -> Weight; fn set_variable_meta_data() -> Weight; + // fn enable_contract_sponsoring() -> Weight; } #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)] @@ -249,7 +250,7 @@ } } -pub trait Trait: system::Trait + Sized { +pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait { type Event: From> + Into<::Event>; /// Weight information for extrinsics in this pallet. @@ -301,9 +302,9 @@ pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec>; pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber; - // Sponsorship - pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId; - pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId; + // Contract Sponsorship and Ownership + pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId; + pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool; } add_extra_genesis { build(|config: &GenesisConfig| { @@ -1251,7 +1252,37 @@ ensure_root(origin)?; ::put(limits); Ok(()) - } + } + + /// Enable smart contract self-sponsoring. + /// + /// # Permissions + /// + /// * Contract Owner + /// + /// # Arguments + /// + /// * contract address + /// * enable flag + /// + #[weight = 0] + pub fn enable_contract_sponsoring( + origin, + contract_address: T::AccountId, + enable: bool + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let mut is_owner = false; + if >::contains_key(contract_address.clone()) { + let owner = >::get(&contract_address); + is_owner = sender == owner; + } + ensure!(is_owner, "Only contract owner may call this method"); + + >::insert(contract_address, enable); + Ok(()) + } + } } @@ -1919,11 +1950,11 @@ /// Require the transactor pay for themselves and maybe include a tip to gain additional priority /// in the queue. #[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct ChargeTransactionPayment( - #[codec(compact)] BalanceOf, +pub struct ChargeTransactionPayment( + #[codec(compact)] BalanceOf ); -impl sp_std::fmt::Debug +impl sp_std::fmt::Debug for ChargeTransactionPayment { #[cfg(feature = "std")] @@ -1936,10 +1967,9 @@ } } -impl ChargeTransactionPayment +impl ChargeTransactionPayment where - T::Call: - Dispatchable + IsSubType>, + T::Call: Dispatchable + IsSubType> + IsSubType>, BalanceOf: Send + Sync + FixedPointOperand, { /// utility constructor. Used only in client/factory code. @@ -1969,15 +1999,16 @@ // Set fee based on call type. Creating collection costs 1 Unique. // All other transactions have traditional fees so far - let fee = match call.is_sub_type() { - Some(Call::create_collection(..)) => >::from(1_000_000_000), - _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes - // _ => >::from(100) - }; + // let fee = match call.is_sub_type() { + // Some(Call::create_collection(..)) => >::from(1_000_000_000), + // _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes + // // _ => >::from(100) + // }; + let fee = Self::traditional_fee(len, info, tip); // Determine who is paying transaction fee based on ecnomic model // Parse call to extract collection ID and access collection sponsor - let sponsor: T::AccountId = match call.is_sub_type() { + let mut sponsor: T::AccountId = match IsSubType::>::is_sub_type(call) { Some(Call::create_item(collection_id, _properties, _owner)) => { >::get(collection_id).sponsor } @@ -2046,6 +2077,39 @@ _ => T::AccountId::default(), }; + // Sponsor smart contracts + sponsor = match IsSubType::>::is_sub_type(call) { + + // On instantiation: set the contract owner + Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => { + + let new_contract_address = ::DetermineContractAddress::contract_address_for( + code_hash, + &data, + &who, + ); + >::insert(new_contract_address.clone(), who.clone()); + + T::AccountId::default() + }, + + // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is + Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => { + + let mut sp = T::AccountId::default(); + let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default()); + if >::contains_key(called_contract.clone()) { + if >::get(called_contract.clone()) { + sp = called_contract; + } + } + + sp + }, + + _ => sponsor, + }; + let mut who_pays_fee: T::AccountId = sponsor.clone(); if sponsor == T::AccountId::default() { who_pays_fee = who.clone(); @@ -2072,11 +2136,12 @@ } } -impl SignedExtension + +impl SignedExtension for ChargeTransactionPayment where BalanceOf: Send + Sync + From + FixedPointOperand, - T::Call: Dispatchable + IsSubType>, + T::Call: Dispatchable + IsSubType> + IsSubType>, { const IDENTIFIER: &'static str = "ChargeTransactionPayment"; type AccountId = T::AccountId; @@ -2094,18 +2159,12 @@ fn validate( &self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, ) -> TransactionValidity { - let (fee, _) = self.withdraw_fee(who, call, info, len)?; - - let mut r = ValidTransaction::default(); - // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which - // will be a bit more than setting the priority to tip. For now, this is enough. - r.priority = fee.saturated_into::(); - Ok(r) + Ok(ValidTransaction::default()) } fn pre_dispatch( @@ -2156,6 +2215,7 @@ Ok(()) } } + // #endregion --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -1,20 +1,27 @@ // Creating mock runtime here use crate::{Module, Trait}; + +use pallet_contracts::{ + ContractAddressFor, TrieId, TrieIdGenerator, +}; + use frame_support::{ impl_outer_origin, parameter_types, weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, - Weight, + // constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}, + Weight, IdentityFee, }, }; use frame_system as system; +use transaction_payment; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup, Saturating}, Perbill, }; +pub use pallet_balances; impl_outer_origin! { pub enum Origin for Test {} @@ -56,13 +63,101 @@ type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type PalletInfo = (); - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } -impl Trait for Test { + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; + pub const MaxLocks: u32 = 50; +} + +type System = frame_system::Module; +impl pallet_balances::Trait for Test { + type AccountStore = System; + type Balance = u64; + type DustRemoval = (); type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type WeightInfo = (); + type MaxLocks = MaxLocks; +} + +parameter_types! { + pub const TransactionByteFee: u64 = 1; +} +impl transaction_payment::Trait for Test { + type Currency = pallet_balances::Module; + type OnTransactionPayment = (); + type TransactionByteFee = TransactionByteFee; + type WeightToFee = IdentityFee; + type FeeMultiplierUpdate = (); +} + + +parameter_types! { + pub const MinimumPeriod: u64 = 1; +} +impl pallet_timestamp::Trait for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +type Timestamp = pallet_timestamp::Module; +type Randomness = pallet_randomness_collective_flip::Module; + +parameter_types! { + pub const TombstoneDeposit: u64 = 1; + pub const RentByteFee: u64 = 1; + pub const RentDepositOffset: u64 = 1; + pub const SurchargeReward: u64 = 1; +} + +pub struct DummyTrieIdGenerator; +impl TrieIdGenerator for DummyTrieIdGenerator { + fn trie_id(account_id: &u64) -> TrieId { + let new_seed = *account_id + 1; + let mut res = vec![]; + res.extend_from_slice(&new_seed.to_le_bytes()); + res.extend_from_slice(&account_id.to_le_bytes()); + res + } +} + +pub struct DummyContractAddressFor; +impl ContractAddressFor for DummyContractAddressFor { + fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 { + *origin + 1 + } +} + +impl pallet_contracts::Trait for Test { + type Time = Timestamp; + type Randomness = Randomness; + type Currency = pallet_balances::Module; + type Event = (); + type DetermineContractAddress = DummyContractAddressFor; + type TrieIdGenerator = DummyTrieIdGenerator; + type RentPayment = (); + type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap; + type TombstoneDeposit = TombstoneDeposit; + type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset; + type RentByteFee = RentByteFee; + type RentDepositOffset = RentDepositOffset; + type SurchargeReward = SurchargeReward; + type MaxDepth = pallet_contracts::DefaultMaxDepth; + type MaxValueSize = pallet_contracts::DefaultMaxValueSize; + type WeightPrice = (); +} + +impl Trait for Test { + type Event = (); + type WeightInfo = (); + } pub type TemplateModule = Module; --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -292,6 +292,77 @@ assert_eq!(TemplateModule::balance_count(1, 1), 1); assert_eq!(TemplateModule::address_tokens(1, 1), [1]); + // neg transfer + assert_noop!(TemplateModule::transfer_from( + origin2.clone(), + 1, + 2, + 1, + 1, + 1), "Only item owner, collection owner and admins can modify items"); + + // do approve + assert_ok!(TemplateModule::approve(origin1.clone(), 2, 1, 1)); + assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 1); + assert_eq!( + TemplateModule::approved(1, (1, 1))[0], + ApprovePermissions { + approved: 2, + amount: 100000000 + } + ); + + assert_ok!(TemplateModule::transfer_from( + origin2.clone(), + 1, + 2, + 1, + 1, + 1 + )); + assert_eq!(TemplateModule::approved(1, (1, 1)).len(), 0); + }); +} + +#[test] +fn nft_approve_and_transfer_from_white_list() { + new_test_ext().execute_with(|| { + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let mode: CollectionMode = CollectionMode::NFT(2000); + + assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits { + collection_numbers_limit: 10, + account_token_ownership_limit: 10, + collections_admins_limit: 5, + custom_data_limit: 2048, + nft_sponsor_transfer_timeout: 15, + fungible_sponsor_transfer_timeout: 15, + refungible_sponsor_transfer_timeout: 15, + })); + + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + assert_ok!(TemplateModule::create_collection( + origin1.clone(), + col_name1.clone(), + col_desc1.clone(), + token_prefix1.clone(), + mode + )); + assert_eq!(TemplateModule::collection(1).owner, 1); + + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 2, 3].to_vec(), + 1 + )); + assert_eq!(TemplateModule::nft_item_id(1, 1).data, [1, 2, 3].to_vec()); + assert_eq!(TemplateModule::balance_count(1, 1), 1); + assert_eq!(TemplateModule::address_tokens(1, 1), [1]); + assert_ok!(TemplateModule::set_mint_permission( origin1.clone(), 1, -- gitstuff