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