git.delta.rocks / unique-network / refs/commits / 3950758e00c3

difftreelog

Merge branch 'develop' into feature/NFTPAR-142

sotmorskiy2020-11-11parents: #f58e687 #6d406a4.patch.diff
in: master
# Conflicts:
#	pallets/nft/src/default_weights.rs
#	pallets/nft/src/lib.rs

6 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3737,6 +3737,7 @@
  "frame-support",
  "frame-system",
  "log",
+ "pallet-contracts",
  "pallet-transaction-payment",
  "parity-scale-codec",
  "serde",
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- 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',
modifiedpallets/nft/src/default_weights.rsdiffbeforeafterboth
--- 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))
+    // }
 }
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- 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<Event<Self>> + Into<<Self as system::Trait>::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<BasketItem<T::AccountId, T::BlockNumber>>;
         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<T>| {
@@ -1251,7 +1252,37 @@
             ensure_root(origin)?;
             <ChainLimit>::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 <ContractOwner<T>>::contains_key(contract_address.clone()) {
+                let owner = <ContractOwner<T>>::get(&contract_address);
+                is_owner = sender == owner;
+            }
+            ensure!(is_owner, "Only contract owner may call this method");
+
+            <ContractSelfSponsoring<T>>::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<T: transaction_payment::Trait + Send + Sync>(
-    #[codec(compact)] BalanceOf<T>,
+pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(
+    #[codec(compact)] BalanceOf<T>
 );
 
-impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug
+impl<T: Trait + Send + Sync> sp_std::fmt::Debug
     for ChargeTransactionPayment<T>
 {
     #[cfg(feature = "std")]
@@ -1936,10 +1967,9 @@
     }
 }
 
-impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>
+impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>
 where
-    T::Call:
-        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,
+    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,
     BalanceOf<T>: 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(..)) => <BalanceOf<T>>::from(1_000_000_000),
-            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes
-                                                        // _ => <BalanceOf<T>>::from(100)
-        };
+        // let fee = match call.is_sub_type() {
+        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),
+        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes
+        //                                                 // _ => <BalanceOf<T>>::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::<Call<T>>::is_sub_type(call) {
             Some(Call::create_item(collection_id, _properties, _owner)) => {
                 <Collection<T>>::get(collection_id).sponsor
             }
@@ -2046,6 +2077,39 @@
             _ => T::AccountId::default(),
         };
 
+        // Sponsor smart contracts
+        sponsor = match IsSubType::<pallet_contracts::Call<T>>::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 = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(
+                    code_hash,
+                    &data,
+                    &who,
+                );
+                <ContractOwner<T>>::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 <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {
+                    if <ContractSelfSponsoring<T>>::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<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension
+
+impl<T: Trait + Send + Sync> SignedExtension
     for ChargeTransactionPayment<T>
 where
     BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,
-    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,
+    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,
 {
     const IDENTIFIER: &'static str = "ChargeTransactionPayment";
     type AccountId = T::AccountId;
@@ -2094,18 +2159,12 @@
 
     fn validate(
         &self,
-        who: &Self::AccountId,
-        call: &Self::Call,
-        info: &DispatchInfoOf<Self::Call>,
-        len: usize,
+        _who: &Self::AccountId,
+        _call: &Self::Call,
+        _info: &DispatchInfoOf<Self::Call>,
+        _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::<TransactionPriority>();
-        Ok(r)
+        Ok(ValidTransaction::default())
     }
 
     fn pre_dispatch(
@@ -2156,6 +2215,7 @@
         Ok(())
     }
 }
+
 // #endregion
 
 
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
before · pallets/nft/src/mock.rs
1// Creating mock runtime here23use crate::{Module, Trait};4use frame_support::{5    impl_outer_origin, parameter_types,6    weights::{7        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},8        Weight,9    },10};11use frame_system as system;12use sp_core::H256;13use sp_runtime::{14    testing::Header,15    traits::{BlakeTwo256, IdentityLookup, Saturating},16    Perbill,17};1819impl_outer_origin! {20    pub enum Origin for Test {}21}2223// For testing the pallet, we construct most of a mock runtime. This means24// first constructing a configuration type (`Test`) which `impl`s each of the25// configuration traits of pallets we want to use.26#[derive(Clone, Eq, PartialEq)]27pub struct Test;28parameter_types! {29    pub const BlockHashCount: u64 = 250;30    pub const MaximumBlockWeight: Weight = 1024;31    pub const MaximumBlockLength: u32 = 2 * 1024;32    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);33    pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()34    .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();35}3637impl system::Trait for Test {38	type BaseCallFilter = ();39	type Origin = Origin;40	type Call = ();41	type Index = u64;42	type BlockNumber = u64;43	type Hash = H256;44	type Hashing = BlakeTwo256;45	type AccountId = u64;46	type Lookup = IdentityLookup<Self::AccountId>;47	type Header = Header;48	type Event = ();49	type BlockHashCount = BlockHashCount;50	type MaximumBlockWeight = MaximumBlockWeight;51	type DbWeight = ();52	type BlockExecutionWeight = ();53	type ExtrinsicBaseWeight = ();54	type MaximumExtrinsicWeight = MaximumBlockWeight;55	type MaximumBlockLength = MaximumBlockLength;56	type AvailableBlockRatio = AvailableBlockRatio;57	type Version = ();58	type PalletInfo = ();59	type AccountData = ();60	type OnNewAccount = ();61	type OnKilledAccount = ();62	type SystemWeightInfo = ();63}64impl Trait for Test {65    type Event = ();66}67pub type TemplateModule = Module<Test>;686970// This function basically just builds a genesis storage key/value store according to71// our desired mockup.72pub fn new_test_ext() -> sp_io::TestExternalities {73    system::GenesisConfig::default()74        .build_storage::<Test>()75        .unwrap()76        .into()77}
after · pallets/nft/src/mock.rs
1// Creating mock runtime here23use crate::{Module, Trait};45use pallet_contracts::{6	ContractAddressFor, TrieId, TrieIdGenerator,7};89use frame_support::{10    impl_outer_origin, parameter_types,11    weights::{12      //  constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},13        Weight, IdentityFee,14    },15};16use frame_system as system;17use transaction_payment;18use sp_core::H256;19use sp_runtime::{20    testing::Header,21    traits::{BlakeTwo256, IdentityLookup, Saturating},22    Perbill,23};24pub use pallet_balances;2526impl_outer_origin! {27    pub enum Origin for Test {}28}2930// For testing the pallet, we construct most of a mock runtime. This means31// first constructing a configuration type (`Test`) which `impl`s each of the32// configuration traits of pallets we want to use.33#[derive(Clone, Eq, PartialEq)]34pub struct Test;35parameter_types! {36    pub const BlockHashCount: u64 = 250;37    pub const MaximumBlockWeight: Weight = 1024;38    pub const MaximumBlockLength: u32 = 2 * 1024;39    pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);40    pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()41    .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();42}4344impl system::Trait for Test {45	type BaseCallFilter = ();46	type Origin = Origin;47	type Call = ();48	type Index = u64;49	type BlockNumber = u64;50	type Hash = H256;51	type Hashing = BlakeTwo256;52	type AccountId = u64;53	type Lookup = IdentityLookup<Self::AccountId>;54	type Header = Header;55	type Event = ();56	type BlockHashCount = BlockHashCount;57	type MaximumBlockWeight = MaximumBlockWeight;58	type DbWeight = ();59	type BlockExecutionWeight = ();60	type ExtrinsicBaseWeight = ();61	type MaximumExtrinsicWeight = MaximumBlockWeight;62	type MaximumBlockLength = MaximumBlockLength;63	type AvailableBlockRatio = AvailableBlockRatio;64	type Version = ();65	type PalletInfo = ();66	type AccountData = pallet_balances::AccountData<u64>;67	type OnNewAccount = ();68	type OnKilledAccount = ();69	type SystemWeightInfo = ();70}7172parameter_types! {73	pub const ExistentialDeposit: u64 = 1;74	pub const MaxLocks: u32 = 50;75}7677type System = frame_system::Module<Test>;78impl pallet_balances::Trait for Test {79    type AccountStore = System;80    type Balance = u64;81    type DustRemoval = ();82    type Event = ();83	type ExistentialDeposit = ExistentialDeposit;84	type WeightInfo = ();85	type MaxLocks = MaxLocks;86}8788parameter_types! {89	pub const TransactionByteFee: u64 = 1;90}91impl transaction_payment::Trait for Test {92	type Currency = pallet_balances::Module<Test>;93	type OnTransactionPayment = ();94	type TransactionByteFee = TransactionByteFee;95	type WeightToFee = IdentityFee<u64>;96	type FeeMultiplierUpdate = ();97}9899100parameter_types! {101	pub const MinimumPeriod: u64 = 1;102}103impl pallet_timestamp::Trait for Test {104	type Moment = u64;105	type OnTimestampSet = ();106	type MinimumPeriod = MinimumPeriod;107	type WeightInfo = ();108}109110type Timestamp = pallet_timestamp::Module<Test>;111type Randomness = pallet_randomness_collective_flip::Module<Test>;112113parameter_types! {114	pub const TombstoneDeposit: u64 = 1;115	pub const RentByteFee: u64 = 1;116	pub const RentDepositOffset: u64 = 1;117	pub const SurchargeReward: u64 = 1;118}119120pub struct DummyTrieIdGenerator;121impl TrieIdGenerator<u64> for DummyTrieIdGenerator {122	fn trie_id(account_id: &u64) -> TrieId {123		let new_seed = *account_id + 1;124		let mut res = vec![];125		res.extend_from_slice(&new_seed.to_le_bytes());126		res.extend_from_slice(&account_id.to_le_bytes());127		res128	}129}130131pub struct DummyContractAddressFor;132impl ContractAddressFor<H256, u64> for DummyContractAddressFor {133	fn contract_address_for(_code_hash: &H256, _data: &[u8], origin: &u64) -> u64 {134		*origin + 1135	}136}137138impl pallet_contracts::Trait for Test {139	type Time = Timestamp;140	type Randomness = Randomness;141	type Currency = pallet_balances::Module<Test>;142	type Event = ();143	type DetermineContractAddress = DummyContractAddressFor;144	type TrieIdGenerator = DummyTrieIdGenerator;145	type RentPayment = ();146	type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;147	type TombstoneDeposit = TombstoneDeposit;148	type StorageSizeOffset = pallet_contracts::DefaultStorageSizeOffset;149	type RentByteFee = RentByteFee;150	type RentDepositOffset = RentDepositOffset;151	type SurchargeReward = SurchargeReward;152	type MaxDepth = pallet_contracts::DefaultMaxDepth;153	type MaxValueSize = pallet_contracts::DefaultMaxValueSize;154	type WeightPrice = ();155}156157impl Trait for Test {158	type Event = ();159	type WeightInfo = ();160161}162pub type TemplateModule = Module<Test>;163164165// This function basically just builds a genesis storage key/value store according to166// our desired mockup.167pub fn new_test_ext() -> sp_io::TestExternalities {168    system::GenesisConfig::default()169        .build_storage::<Test>()170        .unwrap()171        .into()172}
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
--- 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<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
+        let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
+        let token_prefix1: Vec<u8> = 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,