ink_sandbox/
macros.rs

1use std::time::SystemTime;
2
3use frame_support::{
4    sp_runtime::{
5        BuildStorage,
6        traits::{
7            Header,
8            One,
9        },
10    },
11    traits::Hooks,
12};
13use frame_system::pallet_prelude::BlockNumberFor;
14use sp_io::TestExternalities;
15
16/// A helper struct for initializing and finalizing blocks.
17pub struct BlockBuilder<T>(std::marker::PhantomData<T>);
18
19impl<
20    T: pallet_balances::Config
21        + pallet_timestamp::Config<Moment = u64>
22        + pallet_revive::Config,
23> BlockBuilder<T>
24{
25    /// Create a new externalities with the given balances.
26    pub fn new_ext(balances: Vec<(T::AccountId, T::Balance)>) -> TestExternalities {
27        let mut storage = frame_system::GenesisConfig::<T>::default()
28            .build_storage()
29            .unwrap();
30
31        pallet_balances::GenesisConfig::<T> {
32            balances,
33            dev_accounts: None,
34        }
35        .assimilate_storage(&mut storage)
36        .unwrap();
37
38        let mut ext = TestExternalities::new(storage);
39
40        ext.execute_with(|| {
41            Self::initialize_block(BlockNumberFor::<T>::one(), Default::default())
42        });
43        ext
44    }
45
46    /// Initialize a new block at particular height.
47    pub fn initialize_block(
48        height: frame_system::pallet_prelude::BlockNumberFor<T>,
49        parent_hash: <T as frame_system::Config>::Hash,
50    ) {
51        frame_system::Pallet::<T>::reset_events();
52        frame_system::Pallet::<T>::initialize(&height, &parent_hash, &Default::default());
53        pallet_balances::Pallet::<T>::on_initialize(height);
54        pallet_timestamp::Pallet::<T>::set_timestamp(
55            SystemTime::now()
56                .duration_since(SystemTime::UNIX_EPOCH)
57                .expect("Time went backwards")
58                .as_secs(),
59        );
60        pallet_timestamp::Pallet::<T>::on_initialize(height);
61        pallet_revive::Pallet::<T>::on_initialize(height);
62        frame_system::Pallet::<T>::note_finished_initialize();
63    }
64
65    /// Finalize a block at particular height.
66    pub fn finalize_block(
67        height: frame_system::pallet_prelude::BlockNumberFor<T>,
68    ) -> <T as frame_system::Config>::Hash {
69        pallet_revive::Pallet::<T>::on_finalize(height);
70        use sp_core::Get;
71        let minimum_period = <T as pallet_timestamp::Config>::MinimumPeriod::get();
72        let now = pallet_timestamp::Pallet::<T>::get()
73            .checked_add(minimum_period)
74            .unwrap();
75        pallet_timestamp::Pallet::<T>::set_timestamp(now);
76        pallet_timestamp::Pallet::<T>::on_finalize(height);
77        pallet_balances::Pallet::<T>::on_finalize(height);
78        frame_system::Pallet::<T>::finalize().hash()
79    }
80}
81
82/// Macro creating a minimal runtime with the given name.
83///
84/// The new macro will automatically implement `crate::Sandbox`.
85#[macro_export]
86macro_rules! create_sandbox {
87    ($name:ident) => {
88        $crate::paste::paste! {
89            $crate::create_sandbox!($name, [<$name Runtime>], (), {});
90        }
91    };
92    ($name:ident, $debug: ty) => {
93        $crate::paste::paste! {
94            $crate::create_sandbox!($name, [<$name Runtime>], $debug, {});
95        }
96    };
97    ($name:ident, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
98        $crate::paste::paste! {
99            $crate::create_sandbox!($name, [<$name Runtime>], $debug, {
100                $(
101                    $pallet_name : $pallet,
102                )*
103            });
104        }
105    };
106    ($sandbox:ident, $runtime:ident, $debug: ty, { $( $pallet_name:tt : $pallet:ident ),* $(,)? }) => {
107
108// Put all the boilerplate into an auxiliary module
109mod construct_runtime {
110
111    // Bring some common types into the scope
112    use $crate::frame_support::{
113        construct_runtime,
114        derive_impl,
115        parameter_types,
116        sp_runtime::{
117            traits::Convert,
118            AccountId32, Perbill,
119        },
120        traits::{ConstBool, ConstU128, ConstU32, ConstU64, Currency},
121        weights::Weight,
122    };
123
124    use $crate::Snapshot;
125
126    // Define the runtime type as a collection of pallets
127    construct_runtime!(
128        pub enum $runtime {
129            System: $crate::frame_system,
130            Balances: $crate::pallet_balances,
131            Timestamp: $crate::pallet_timestamp,
132            Revive: $crate::pallet_revive,
133            $(
134                $pallet_name: $pallet,
135            )*
136        }
137    );
138
139    // Configure pallet system
140    #[derive_impl($crate::frame_system::config_preludes::SolochainDefaultConfig as $crate::frame_system::DefaultConfig)]
141    impl $crate::frame_system::Config for $runtime {
142        type Block = $crate::frame_system::mocking::MockBlockU32<$runtime>;
143        type Version = ();
144        type BlockHashCount = ConstU32<250>;
145        type AccountData = $crate::pallet_balances::AccountData<<$runtime as $crate::pallet_balances::Config>::Balance>;
146    }
147
148    // Configure pallet balances
149    impl $crate::pallet_balances::Config for $runtime {
150        type RuntimeEvent = RuntimeEvent;
151        type WeightInfo = ();
152        type Balance = u128;
153        type DustRemoval = ();
154        type ExistentialDeposit = ConstU128<1>;
155        type AccountStore = System;
156        type ReserveIdentifier = [u8; 8];
157        type FreezeIdentifier = ();
158        type MaxLocks = ();
159        type MaxReserves = ();
160        type MaxFreezes = ();
161        type RuntimeHoldReason = RuntimeHoldReason;
162        type RuntimeFreezeReason = RuntimeFreezeReason;
163        type DoneSlashHandler = ();
164    }
165
166    // Configure pallet timestamp
167    impl $crate::pallet_timestamp::Config for $runtime {
168        type Moment = u64;
169        type OnTimestampSet = ();
170        type MinimumPeriod = ConstU64<1>;
171        type WeightInfo = ();
172    }
173
174    // Configure pallet revive
175    type BalanceOf = <Balances as Currency<AccountId32>>::Balance;
176    impl Convert<Weight, BalanceOf> for $runtime {
177        fn convert(w: Weight) -> BalanceOf {
178            w.ref_time().into()
179        }
180    }
181
182    parameter_types! {
183        pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
184    }
185
186    impl $crate::pallet_revive::Config for $runtime {
187        type AddressMapper = $crate::pallet_revive::AccountId32Mapper<Self>;
188        type ChainId = ConstU64<1>;
189        type NativeToEthRatio = ConstU32<100_000_000>;
190        type Time = Timestamp;
191        type Currency = Balances;
192        type RuntimeEvent = RuntimeEvent;
193        type RuntimeCall = RuntimeCall;
194        type DepositPerItem = ConstU128<1>;
195        type DepositPerByte = ConstU128<1>;
196        type WeightPrice = Self;
197        type WeightInfo = ();
198        type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
199        type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
200        type UnsafeUnstableInterface = ConstBool<true>;
201        type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
202        type RuntimeHoldReason = RuntimeHoldReason;
203        type UploadOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
204        type InstantiateOrigin = $crate::frame_system::EnsureSigned<Self::AccountId>;
205        type EthGasEncoder = ();
206        type FindAuthor = ();
207        type Precompiles = (
208            // todo
209            //ERC20<Self, InlineIdConfig<0x120>, TrustBackedAssetsInstance>,
210            //ERC20<Self, InlineIdConfig<0x320>, PoolAssetsInstance>,
211            //XcmPrecompile<Self>,
212        );
213        type AllowEVMBytecode = ConstBool<false>;
214    }
215
216    /// Default initial balance for the default account.
217    pub const INITIAL_BALANCE: u128 = 1_000_000_000_000_000;
218    pub const DEFAULT_ACCOUNT: AccountId32 = AccountId32::new([1u8; 32]);
219
220    pub struct $sandbox {
221        ext: $crate::TestExternalities,
222    }
223
224    impl ::std::default::Default for $sandbox {
225        fn default() -> Self {
226            let ext = $crate::macros::BlockBuilder::<$runtime>::new_ext(vec![(
227                DEFAULT_ACCOUNT,
228                INITIAL_BALANCE,
229            )]);
230            Self { ext }
231        }
232    }
233
234    // Implement `crate::Sandbox` trait
235    impl $crate::Sandbox for $sandbox {
236        type Runtime = $runtime;
237
238        fn execute_with<T>(&mut self, execute: impl FnOnce() -> T) -> T {
239            self.ext.execute_with(execute)
240        }
241
242        fn dry_run<T>(&mut self, action: impl FnOnce(&mut Self) -> T) -> T {
243            // Make a backup of the backend.
244            let backend_backup = self.ext.as_backend();
245            // Run the action, potentially modifying storage. Ensure, that there are no pending changes
246            // that would affect the reverted backend.
247            let result = action(self);
248            self.ext.commit_all().expect("Failed to commit changes");
249
250            // Restore the backend.
251            self.ext.backend = backend_backup;
252            result
253        }
254
255        fn register_extension<E: ::core::any::Any + $crate::Extension>(&mut self, ext: E) {
256            self.ext.register_extension(ext);
257        }
258
259        fn initialize_block(
260            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
261            parent_hash: <Self::Runtime as $crate::frame_system::Config>::Hash,
262        ) {
263            $crate::macros::BlockBuilder::<Self::Runtime>::initialize_block(height, parent_hash)
264        }
265
266        fn finalize_block(
267            height: $crate::frame_system::pallet_prelude::BlockNumberFor<Self::Runtime>,
268        ) -> <Self::Runtime as $crate::frame_system::Config>::Hash {
269            $crate::macros::BlockBuilder::<Self::Runtime>::finalize_block(height)
270        }
271
272        fn default_actor() -> $crate::AccountIdFor<Self::Runtime> {
273            DEFAULT_ACCOUNT
274        }
275
276        fn get_metadata() -> $crate::RuntimeMetadataPrefixed {
277            Self::Runtime::metadata()
278        }
279
280        fn convert_account_to_origin(
281            account: $crate::AccountIdFor<Self::Runtime>,
282        ) -> <<Self::Runtime as $crate::frame_system::Config>::RuntimeCall as $crate::frame_support::sp_runtime::traits::Dispatchable>::RuntimeOrigin {
283            Some(account).into()
284        }
285
286        fn take_snapshot(&mut self) -> Snapshot {
287            let mut backend = self.ext.as_backend().clone();
288            let raw_key_values = backend
289                .backend_storage_mut()
290                .drain()
291                .into_iter()
292                .filter(|(_, (_, r))| *r > 0)
293                .collect::<Vec<(Vec<u8>, (Vec<u8>, i32))>>();
294            let root = backend.root().to_owned();
295            Snapshot {
296                storage: raw_key_values,
297                storage_root: root,
298            }
299        }
300
301        fn restore_snapshot(&mut self, snapshot: Snapshot) {
302            self.ext = $crate::TestExternalities::from_raw_snapshot(
303                snapshot.storage,
304                snapshot.storage_root,
305                Default::default(),
306            );
307        }
308    }
309}
310
311// Export runtime type itself, pallets and useful types from the auxiliary module
312pub use construct_runtime::{
313    $sandbox, $runtime, Balances, Revive, PalletInfo, RuntimeCall, RuntimeEvent, RuntimeHoldReason,
314    RuntimeOrigin, System, Timestamp,
315};
316    };
317}
318
319create_sandbox!(DefaultSandbox);