ink_sandbox/
lib.rs

1use core::any::Any;
2
3pub mod api;
4pub mod client;
5mod error;
6pub mod macros;
7
8pub use frame_metadata::RuntimeMetadataPrefixed;
9pub use frame_support::weights::Weight;
10use frame_support::{
11    sp_runtime::traits::Dispatchable,
12    traits::fungible::Inspect,
13};
14use frame_system::{
15    EventRecord,
16    pallet_prelude::{
17        BlockNumberFor,
18        OriginFor,
19    },
20};
21use ink_primitives::U256;
22use ink_revive_types::{
23    Bytes,
24    evm::{
25        CallLog,
26        CallTrace,
27    },
28};
29pub use macros::{
30    BlockBuilder,
31    DefaultSandbox,
32};
33use pallet_revive::{
34    ContractResult,
35    ExecReturnValue,
36    InstantiateReturnValue,
37};
38use sp_core::Get;
39/// Export pallets that are used in [`crate::create_sandbox`]
40pub use {
41    frame_support::sp_runtime::testing::H256,
42    frame_support::{
43        self,
44        sp_runtime::{
45            AccountId32,
46            DispatchError,
47        },
48    },
49    frame_system,
50    pallet_balances,
51    pallet_revive,
52    pallet_timestamp,
53    pallet_transaction_payment,
54    paste,
55    sp_core::crypto::Ss58Codec,
56    sp_externalities::{
57        self,
58        Extension,
59    },
60    sp_io::TestExternalities,
61};
62
63pub use client::{
64    Client as SandboxClient,
65    preset,
66};
67pub use ink_e2e_macro::test;
68
69/// A snapshot of the storage.
70#[derive(Clone, Debug)]
71pub struct Snapshot {
72    /// The storage raw key-value pairs.
73    pub storage: RawStorage,
74    /// The storage root hash.
75    pub storage_root: StorageRoot,
76}
77
78pub type RawStorage = Vec<(Vec<u8>, (Vec<u8>, i32))>;
79pub type StorageRoot = H256;
80
81/// Alias for the balance type.
82type BalanceOf<R> =
83    <<R as pallet_revive::Config>::Currency as Inspect<AccountIdFor<R>>>::Balance;
84
85/// Alias for the account ID type.
86pub type AccountIdFor<R> = <R as frame_system::Config>::AccountId;
87
88/// Alias for the runtime call type.
89pub type RuntimeCall<R> = <R as frame_system::Config>::RuntimeCall;
90
91/// Alias for the event record type.
92pub type EventRecordOf<Runtime> = EventRecord<
93    <Runtime as frame_system::Config>::RuntimeEvent,
94    <Runtime as frame_system::Config>::Hash,
95>;
96
97/// Alias for the contract instantiate result.
98pub type ContractInstantiateResultFor<Runtime> =
99    ContractResult<OriginFor<Runtime>, BalanceOf<Runtime>>;
100
101pub type ContractResultFor<Runtime> = ContractResult<Runtime, BalanceOf<Runtime>>;
102
103pub type ContractResultInstantiate<Runtime> =
104    ContractResult<InstantiateReturnValue, BalanceOf<Runtime>>;
105
106/// Alias for the contract exec result.
107pub type ContractExecResultFor<Runtime> =
108    ContractResult<ExecReturnValue, BalanceOf<Runtime>>;
109
110/// Alias for the `map_account` result.
111pub type MapAccountResultFor = Result<(), DispatchError>;
112
113/// Alias for the runtime of a sandbox.
114pub type RuntimeOf<S> = <S as Sandbox>::Runtime;
115
116/// Alias for the runtime event of a sandbox.
117pub type RuntimeEventOf<S> = <RuntimeOf<S> as frame_system::Config>::RuntimeEvent;
118
119/// Sandbox defines the API of a sandboxed runtime.
120pub trait Sandbox {
121    /// The runtime associated with the sandbox.
122    type Runtime: frame_system::Config;
123
124    /// Execute the given externalities.
125    fn execute_with<T>(&mut self, execute: impl FnOnce() -> T) -> T;
126
127    /// Dry run an action without modifying the storage.
128    fn dry_run<T>(&mut self, action: impl FnOnce(&mut Self) -> T) -> T;
129
130    /// Register an extension.
131    fn register_extension<E: Any + Extension>(&mut self, ext: E);
132
133    /// Initialize a new block at particular height.
134    fn initialize_block(
135        _height: BlockNumberFor<Self::Runtime>,
136        _parent_hash: <Self::Runtime as frame_system::Config>::Hash,
137    ) {
138    }
139
140    /// Finalize a block at particular height.
141    fn finalize_block(
142        _height: BlockNumberFor<Self::Runtime>,
143    ) -> <Self::Runtime as frame_system::Config>::Hash {
144        Default::default()
145    }
146
147    /// Default actor for the sandbox.
148    fn default_actor() -> AccountIdFor<Self::Runtime>;
149
150    fn default_gas_limit() -> Weight {
151        Weight::from_parts(100_000_000_000_000, 6 * 1024 * 1024)
152    }
153
154    /// Metadata of the runtime.
155    fn get_metadata() -> RuntimeMetadataPrefixed;
156
157    /// Convert an account to a call origin.
158    fn convert_account_to_origin(
159        account: AccountIdFor<Self::Runtime>,
160    ) -> <<Self::Runtime as frame_system::Config>::RuntimeCall as Dispatchable>::RuntimeOrigin;
161
162    /// Take a snapshot of the storage.
163    fn take_snapshot(&mut self) -> Snapshot;
164
165    /// Restore the storage from the given snapshot.
166    fn restore_snapshot(&mut self, snapshot: Snapshot);
167}
168
169/// Converts from the generic `Balance` type to the Ethereum native `U256`.
170///
171/// # Developer Note
172///
173/// `pallet-revive` uses both types, hence we have to convert in between them
174/// for certain functions. Notice that precision loss might occur when converting
175/// the other way (from `U256` to `Balance`).
176///
177/// See <https://github.com/paritytech/polkadot-sdk/pull/9101> for more details.
178pub fn balance_to_evm_value<R>(value: BalanceOf<R>) -> U256
179where
180    R: pallet_revive::Config,
181    BalanceOf<R>: Into<U256>,
182    U256: From<u32>,
183{
184    let native_to_eth_ratio: U256 =
185        <R as pallet_revive::Config>::NativeToEthRatio::get().into();
186    let evm_value: U256 = value.into();
187    native_to_eth_ratio.saturating_mul(evm_value)
188}
189
190/// Convert a `pallet_revive::CallTrace` (sandbox) into an `ink_revive_types::CallTrace`
191/// (API).
192pub fn to_revive_trace(t: pallet_revive::evm::CallTrace) -> CallTrace {
193    CallTrace {
194        from: t.from,
195        gas: t.gas,
196        gas_used: t.gas_used,
197        to: t.to,
198        input: Bytes(t.input.0),
199        output: Bytes(t.output.0),
200        error: t.error,
201        revert_reason: t.revert_reason,
202        calls: t.calls.into_iter().map(to_revive_trace).collect(),
203        logs: t
204            .logs
205            .into_iter()
206            .map(|log| {
207                CallLog {
208                    address: log.address,
209                    topics: log.topics,
210                    data: log.data.0,
211                    ..Default::default()
212                }
213            })
214            .collect(),
215        value: t.value,
216        call_type: to_revive_call_type(t.call_type),
217        child_call_count: t.child_call_count,
218    }
219}
220
221/// Convert a `pallet_revive::CallType` into an `ink_revive_types::evm::CallType`.
222fn to_revive_call_type(
223    ct: pallet_revive::evm::CallType,
224) -> ink_revive_types::evm::CallType {
225    match ct {
226        pallet_revive::evm::CallType::Call => ink_revive_types::evm::CallType::Call,
227        pallet_revive::evm::CallType::StaticCall => {
228            ink_revive_types::evm::CallType::StaticCall
229        }
230        pallet_revive::evm::CallType::DelegateCall => {
231            ink_revive_types::evm::CallType::DelegateCall
232        }
233        pallet_revive::evm::CallType::Create => ink_revive_types::evm::CallType::Create,
234        pallet_revive::evm::CallType::Create2 => ink_revive_types::evm::CallType::Create2,
235    }
236}
237
238/// Convert a `pallet_revive::StorageDeposit` into an `ink_revive_types::StorageDeposit`.
239pub fn to_revive_storage_deposit<B>(
240    sd: pallet_revive::StorageDeposit<B>,
241) -> ink_revive_types::StorageDeposit<B> {
242    match sd {
243        pallet_revive::StorageDeposit::Charge(b) => {
244            ink_revive_types::StorageDeposit::Charge(b)
245        }
246        pallet_revive::StorageDeposit::Refund(b) => {
247            ink_revive_types::StorageDeposit::Refund(b)
248        }
249    }
250}