ink_sandbox/
lib.rs

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