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