git.delta.rocks / unique-network / refs/commits / bc320b6f043d

difftreelog

refactor extract contracts logic to separate pallet

Yaroslav Bolyukin2021-06-24parent: #ee920c9.patch.diff
in: master

2 files changed

addedpallets/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(())
+		}
+	}
+
+}
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
344 /// 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 Ownership
348 /// 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 //#endregion
361 }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 }
1238
1239 /// Enable smart contract self-sponsoring.
1240 ///
1241 /// # Permissions
1242 ///
1243 /// * Contract Owner
1244 ///
1245 /// # Arguments
1246 ///
1247 /// * contract address
1248 /// * enable flag
1249 ///
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: bool
1256 ) -> DispatchResult {
1257
1258 let sender = ensure_signed(origin)?;
1259
1260 #[cfg(feature = "runtime-benchmarks")]
1261 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());
1262
1263 Self::ensure_contract_owned(sender, &contract_address)?;
1264
1265 <ContractSelfSponsoring<T>>::insert(contract_address, enable);
1266 Ok(())
1267 }
1268
1269 /// 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 /// # Permissions
1278 ///
1279 /// * Contract Owner
1280 ///
1281 /// # Arguments
1282 ///
1283 /// -`contract_address`: Address of the contract to sponsor
1284 /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed
1285 ///
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::BlockNumber
1292 ) -> DispatchResult {
1293 let sender = ensure_signed(origin)?;
1294
1295 #[cfg(feature = "runtime-benchmarks")]
1296 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());
1297
1298 Self::ensure_contract_owned(sender, &contract_address)?;
1299 <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);
1300 Ok(())
1301 }
1302
1303 /// 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 /// # Permissions
1306 ///
1307 /// * Address that deployed smart contract.
1308 ///
1309 /// # Arguments
1310 ///
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: bool
1320 ) -> DispatchResult {
1321 let sender = ensure_signed(origin)?;
1322
1323 #[cfg(feature = "runtime-benchmarks")]
1324 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());
1325
1326 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 /// # Permissions
1338 ///
1339 /// * Address that deployed smart contract.
1340 ///
1341 /// # Arguments
1342 ///
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::AccountId
1352 ) -> DispatchResult {
1353 let sender = ensure_signed(origin)?;
1354
1355 #[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 }
1362
1363 /// Remove an address from smart contract white list.
1364 ///
1365 /// # Permissions
1366 ///
1367 /// * Address that deployed smart contract.
1368 ///
1369 /// # Arguments
1370 ///
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::AccountId
1380 ) -> DispatchResult {
1381 let sender = ensure_signed(origin)?;
1382
1383 #[cfg(feature = "runtime-benchmarks")]
1384 <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());
1385
1386 Self::ensure_contract_owned(sender, &contract_address)?;
1387 <ContractWhiteList<T>>::remove(contract_address, account_address);
1388 Ok(())
1389 }
13901223
1391 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1224 #[weight = <T as Config>::WeightInfo::set_collection_limits()]
1392 #[transactional]1225 #[transactional]