From d8a74901462ffb49345af6db7c5a7a6e2b3c92ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= <alex.theissen@me.com>
Date: Wed, 23 Aug 2023 13:05:35 +0200
Subject: [PATCH] contracts: Expose environment types for offchain tooling
 (#14750)

* Expose environment types for offchain tooling

* Use EnvironmentType wrapper

* Add type impl to test config

---------

Co-authored-by: parity-processbot <>
---
 substrate/bin/node/runtime/src/lib.rs  |  1 +
 substrate/frame/contracts/src/lib.rs   | 47 ++++++++++++++++++++++++--
 substrate/frame/contracts/src/tests.rs |  1 +
 3 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index 4e1b6d4e8be..87ec467dca9 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -1265,6 +1265,7 @@ impl pallet_contracts::Config for Runtime {
 	type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
 	#[cfg(feature = "unsafe-debug")]
 	type Debug = ();
+	type Environment = ();
 }
 
 impl pallet_sudo::Config for Runtime {
diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs
index a511ac81a73..c6a43779ada 100644
--- a/substrate/frame/contracts/src/lib.rs
+++ b/substrate/frame/contracts/src/lib.rs
@@ -103,7 +103,7 @@ pub mod weights;
 #[cfg(test)]
 mod tests;
 use crate::{
-	exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, Stack as ExecStack},
+	exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Key, MomentOf, Stack as ExecStack},
 	gas::GasMeter,
 	storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
 	wasm::{CodeInfo, WasmBlob},
@@ -122,9 +122,13 @@ use frame_support::{
 		ConstU32, Contains, Get, Randomness, Time,
 	},
 	weights::Weight,
-	BoundedVec, RuntimeDebug, RuntimeDebugNoBound,
+	BoundedVec, DefaultNoBound, RuntimeDebug, RuntimeDebugNoBound,
+};
+use frame_system::{
+	ensure_signed,
+	pallet_prelude::{BlockNumberFor, OriginFor},
+	EventRecord, Pallet as System,
 };
-use frame_system::{ensure_signed, pallet_prelude::OriginFor, EventRecord, Pallet as System};
 use pallet_contracts_primitives::{
 	Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult,
 	ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult,
@@ -179,6 +183,36 @@ const SENTINEL: u32 = u32::MAX;
 /// Example: `RUST_LOG=runtime::contracts=debug my_code --dev`
 const LOG_TARGET: &str = "runtime::contracts";
 
+/// Wrapper around `PhantomData` to prevent it being filtered by `scale-info`.
+///
+/// `scale-info` filters out `PhantomData` fields because usually we are only interested
+/// in sized types. However, when trying to communicate **types** as opposed to **values**
+/// we want to have those zero sized types be included.
+#[derive(Encode, Decode, DefaultNoBound, TypeInfo)]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+pub struct EnvironmentType<T>(PhantomData<T>);
+
+/// List of all runtime configurable types that are used in the communication between
+/// `pallet-contracts` and any given contract.
+///
+/// Since those types are configurable they can vary between
+/// chains all using `pallet-contracts`. Hence we need a mechanism to communicate those types
+/// in a way that can be consumed by offchain tooling.
+///
+/// This type only exists in order to appear in the metadata where it can be read by
+/// offchain tooling.
+#[derive(Encode, Decode, DefaultNoBound, TypeInfo)]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[scale_info(skip_type_params(T))]
+pub struct Environment<T: Config> {
+	account_id: EnvironmentType<AccountIdOf<T>>,
+	balance: EnvironmentType<BalanceOf<T>>,
+	hash: EnvironmentType<<T as frame_system::Config>::Hash>,
+	hasher: EnvironmentType<<T as frame_system::Config>::Hashing>,
+	timestamp: EnvironmentType<MomentOf<T>>,
+	block_number: EnvironmentType<BlockNumberFor<T>>,
+}
+
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
@@ -360,6 +394,13 @@ pub mod pallet {
 		/// Do **not** use it in a production environment or for benchmarking purposes.
 		#[cfg(feature = "unsafe-debug")]
 		type Debug: unsafe_debug::UnsafeDebug<Self>;
+
+		/// Type that bundles together all the runtime configurable interface types.
+		///
+		/// This is not a real config. We just mention the type here as constant so that
+		/// its type appears in the metadata. Only valid value is `()`.
+		#[pallet::constant]
+		type Environment: Get<Environment<Self>>;
 	}
 
 	#[pallet::hooks]
diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs
index 5c8fca43ab7..b421bbf3e30 100644
--- a/substrate/frame/contracts/src/tests.rs
+++ b/substrate/frame/contracts/src/tests.rs
@@ -481,6 +481,7 @@ impl Config for Test {
 	type MaxDelegateDependencies = MaxDelegateDependencies;
 	#[cfg(feature = "unsafe-debug")]
 	type Debug = unsafe_debug::TestDebugger;
+	type Environment = ();
 }
 
 pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]);
-- 
GitLab