--- /dev/null +++ b/pallets/contract-helpers/src/lib.rs @@ -0,0 +1,230 @@ +#![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 { + /// 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(_); + + #[pallet::storage] + pub(super) type Owner = StorageMap< + Hasher = Twox128, + Key = T::AccountId, + Value = T::AccountId, + QueryKind = ValueQuery, + >; + + #[pallet::storage] + pub(super) type AllowlistEnabled = + StorageMap; + + #[pallet::storage] + pub(super) type Allowlist = StorageDoubleMap< + Hasher1 = Twox128, + Key1 = T::AccountId, + Hasher2 = Twox64Concat, + Key2 = T::AccountId, + Value = bool, + QueryKind = ValueQuery, + >; + + #[pallet::storage] + pub(super) type SelfSponsoring = + StorageMap; + + #[pallet::storage] + pub(super) type SponsoringRateLimit = StorageMap< + Hasher = Twox128, + Key = T::AccountId, + Value = T::BlockNumber, + QueryKind = ValueQuery, + >; + + #[pallet::storage] + pub(super) type SponsorBasket = StorageDoubleMap< + Hasher1 = Twox128, + Key1 = T::AccountId, + Hasher2 = Twox128, + Key2 = T::AccountId, + Value = T::BlockNumber, + QueryKind = ValueQuery, + >; + + #[pallet::call] + impl Pallet { + #[pallet::weight(0)] + fn toggle_sponsoring( + origin: OriginFor, + contract: T::AccountId, + sponsoring: bool, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(>::get(&contract) == sender, >::NoPermission); + + if sponsoring { + >::insert(contract, true); + } else { + >::remove(contract); + } + Ok(()) + } + + #[pallet::weight(0)] + fn toggle_allowlist( + origin: OriginFor, + contract: T::AccountId, + enabled: bool, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(>::get(&contract) == sender, >::NoPermission); + + if enabled { + >::insert(contract, true); + } else { + >::remove(contract); + } + Ok(()) + } + + #[pallet::weight(0)] + fn toggle_allowed( + origin: OriginFor, + contract: T::AccountId, + user: T::AccountId, + allowed: bool, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(>::get(&contract) == sender, >::NoPermission); + + if allowed { + >::insert(contract, user, true); + } else { + >::remove(contract, user); + } + Ok(()) + } + + #[pallet::weight(0)] + fn set_sponsoring_rate_limit( + origin: OriginFor, + contract: T::AccountId, + rate_limit: T::BlockNumber, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + ensure!(>::get(&contract) == sender, >::NoPermission); + + >::insert(contract, rate_limit); + Ok(()) + } + } + + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub struct ContractHelpersExtension(PhantomData); + impl core::fmt::Debug for ContractHelpersExtension { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + fmt.debug_struct("ContractHelpersExtension").finish() + } + } + + type CodeHash = ::Hash; + impl SignedExtension for ContractHelpersExtension + where + T: Config + Send + Sync, + T::Call: sp_runtime::traits::Dispatchable, + T::Call: IsSubType>, + T::AccountId: UncheckedFrom, + 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, Vec)>; + + fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> { + Ok(()) + } + + fn validate( + &self, + who: &T::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> transaction_validity::TransactionValidity { + match IsSubType::>::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 >::get(&called_contract) { + if !>::get(&called_contract, who) + && &>::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, + _len: usize, + ) -> Result { + match IsSubType::>::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, + _post_info: &PostDispatchInfoOf, + _len: usize, + _result: &DispatchResult, + ) -> Result<(), TransactionValidityError> { + if let Some((who, code_hash, salt)) = pre { + let new_contract_address = + >::contract_address(&who, &code_hash, &salt); + >::insert(&new_contract_address, &who); + } + + Ok(()) + } + } + +} --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -343,21 +343,6 @@ /// Variable metadata sponsoring /// Collection id (controlled?2), token id (controlled?2) pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option = None; - - //#region Contract Sponsorship and Ownership - /// Contract address (real) - pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option; - /// Contract address (real) - pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool; - /// (Contract address(real), caller (real)) - pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber; - /// Contract address (real) - pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber; - /// Contract address (real) - pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; - /// Contract address (real) => Whitelisted user (controlled?3) - pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; - //#endregion } add_extra_genesis { build(|config: &GenesisConfig| { @@ -1233,158 +1218,6 @@ ensure_root(origin)?; ::put(limits); - Ok(()) - } - - /// Enable smart contract self-sponsoring. - /// - /// # Permissions - /// - /// * Contract Owner - /// - /// # Arguments - /// - /// * contract address - /// * enable flag - /// - #[weight = ::WeightInfo::enable_contract_sponsoring()] - #[transactional] - pub fn enable_contract_sponsoring( - origin, - contract_address: T::AccountId, - enable: bool - ) -> DispatchResult { - - let sender = ensure_signed(origin)?; - - #[cfg(feature = "runtime-benchmarks")] - >::insert(contract_address.clone(), sender.clone()); - - Self::ensure_contract_owned(sender, &contract_address)?; - - >::insert(contract_address, enable); - Ok(()) - } - - /// Set the rate limit for contract sponsoring to specified number of blocks. - /// - /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. - /// If set to the number B (for blocks), the transactions will be sponsored with a rate - /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid - /// from contract endowment if there are at least B blocks between such transactions. - /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender. - /// - /// # Permissions - /// - /// * Contract Owner - /// - /// # Arguments - /// - /// -`contract_address`: Address of the contract to sponsor - /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed - /// - #[weight = ::WeightInfo::set_contract_sponsoring_rate_limit()] - #[transactional] - pub fn set_contract_sponsoring_rate_limit( - origin, - contract_address: T::AccountId, - rate_limit: T::BlockNumber - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - #[cfg(feature = "runtime-benchmarks")] - >::insert(contract_address.clone(), sender.clone()); - - Self::ensure_contract_owned(sender, &contract_address)?; - >::insert(contract_address, rate_limit); - Ok(()) - } - - /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract. - /// - /// # Permissions - /// - /// * Address that deployed smart contract. - /// - /// # Arguments - /// - /// -`contract_address`: Address of the contract. - /// - /// - `enable`: . - #[weight = ::WeightInfo::toggle_contract_white_list()] - #[transactional] - pub fn toggle_contract_white_list( - origin, - contract_address: T::AccountId, - enable: bool - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - #[cfg(feature = "runtime-benchmarks")] - >::insert(contract_address.clone(), sender.clone()); - - Self::ensure_contract_owned(sender, &contract_address)?; - if enable { - >::insert(contract_address, true); - } else { - >::remove(contract_address); - } - Ok(()) - } - - /// Add an address to smart contract white list. - /// - /// # Permissions - /// - /// * Address that deployed smart contract. - /// - /// # Arguments - /// - /// -`contract_address`: Address of the contract. - /// - /// -`account_address`: Address to add. - #[weight = ::WeightInfo::add_to_contract_white_list()] - #[transactional] - pub fn add_to_contract_white_list( - origin, - contract_address: T::AccountId, - account_address: T::AccountId - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - #[cfg(feature = "runtime-benchmarks")] - >::insert(contract_address.clone(), sender.clone()); - - Self::ensure_contract_owned(sender, &contract_address)?; - >::insert(contract_address, account_address, true); - Ok(()) - } - - /// Remove an address from smart contract white list. - /// - /// # Permissions - /// - /// * Address that deployed smart contract. - /// - /// # Arguments - /// - /// -`contract_address`: Address of the contract. - /// - /// -`account_address`: Address to remove. - #[weight = ::WeightInfo::remove_from_contract_white_list()] - #[transactional] - pub fn remove_from_contract_white_list( - origin, - contract_address: T::AccountId, - account_address: T::AccountId - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - - #[cfg(feature = "runtime-benchmarks")] - >::insert(contract_address.clone(), sender.clone()); - - Self::ensure_contract_owned(sender, &contract_address)?; - >::remove(contract_address, account_address); Ok(()) }