difftreelog
refactor extract contracts logic to separate pallet
in: master
2 files changed
pallets/contract-helpers/src/lib.rsdiffbeforeafterboth--- /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<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(())
+ }
+ }
+
+}
pallets/nft/src/lib.rsdiffbeforeafterboth344 /// Collection id (controlled?2), token id (controlled?2)344 /// 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;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 }346 }362 add_extra_genesis {347 add_extra_genesis {363 build(|config: &GenesisConfig<T>| {348 build(|config: &GenesisConfig<T>| {1236 Ok(())1221 Ok(())1237 }1222 }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 }139012231391 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1224 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1392 #[transactional]1225 #[transactional]