ink_e2e/
backend.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::{
16    InstantiateDryRunResult,
17    Keypair,
18    H256,
19};
20use crate::{
21    backend_calls::{
22        InstantiateBuilder,
23        RemoveCodeBuilder,
24        UploadBuilder,
25    },
26    builders::CreateBuilderPartial,
27    contract_results::BareInstantiationResult,
28    CallBuilder,
29    CallBuilderFinal,
30    CallDryRunResult,
31    UploadResult,
32};
33use ink_env::{
34    DefaultEnvironment,
35    Environment,
36};
37use ink_primitives::{
38    reflect::{
39        AbiDecodeWith,
40        AbiEncodeWith,
41        ScaleEncoding,
42    },
43    DepositLimit,
44};
45use jsonrpsee::core::async_trait;
46use pallet_revive::evm::CallTrace;
47use sp_weights::Weight;
48use subxt::dynamic::Value;
49
50/// Full E2E testing backend: combines general chain API and contract-specific operations.
51#[async_trait]
52pub trait E2EBackend<E: Environment = DefaultEnvironment>:
53    ChainBackend + BuilderClient<E>
54{
55}
56
57/// General chain operations useful in contract testing.
58#[async_trait]
59pub trait ChainBackend {
60    /// Account type.
61    type AccountId;
62    /// Balance type.
63    type Balance: Send + From<u32>;
64    /// Error type.
65    type Error;
66    /// Event log type.
67    type EventLog;
68
69    /// Generate a new account and fund it with the given `amount` of tokens from the
70    /// `origin`.
71    async fn create_and_fund_account(
72        &mut self,
73        origin: &Keypair,
74        amount: Self::Balance,
75    ) -> Keypair;
76
77    /// Returns the free balance of `account`.
78    async fn free_balance(
79        &mut self,
80        account: Self::AccountId,
81    ) -> Result<Self::Balance, Self::Error>;
82
83    /// Executes a runtime call `call_name` for the `pallet_name`.
84    /// The `call_data` is a `Vec<Value>`.
85    ///
86    /// Note:
87    /// - `pallet_name` must be in camel case, for example `Balances`.
88    /// - `call_name` must be snake case, for example `force_transfer`.
89    /// - `call_data` is a `Vec<subxt::dynamic::Value>` that holds a representation of
90    ///   some value.
91    ///
92    /// Returns when the transaction is included in a block. The return value contains all
93    /// events that are associated with this transaction.
94    ///
95    /// Since we might run node with an arbitrary runtime, this method inherently must
96    /// support dynamic calls.
97    async fn runtime_call<'a>(
98        &mut self,
99        origin: &Keypair,
100        pallet_name: &'a str,
101        call_name: &'a str,
102        call_data: Vec<Value>,
103    ) -> Result<Self::EventLog, Self::Error>;
104}
105
106/// Contract-specific operations.
107#[async_trait]
108pub trait ContractsBackend<E: Environment> {
109    /// Error type.
110    type Error;
111    /// Event log type.
112    type EventLog;
113
114    /// Start building an instantiate call using a builder pattern.
115    ///
116    /// # Example
117    ///
118    /// ```ignore
119    /// // Constructor method
120    /// let mut constructor = FlipperRef::new(false);
121    /// let contract = client
122    ///     .instantiate("flipper", &ink_e2e::alice(), &mut constructor)
123    ///     // Optional arguments
124    ///     // Send 100 units with the call.
125    ///     .value(100)
126    ///     // Add 10% margin to the gas limit
127    ///     .extra_gas_portion(10)
128    ///     .storage_deposit_limit(100)
129    ///     // Submit the call for on-chain execution.
130    ///     .submit()
131    ///     .await
132    ///     .expect("instantiate failed");
133    /// ```
134    fn instantiate<
135        'a,
136        Contract: Clone,
137        Args: Send + Clone + AbiEncodeWith<ScaleEncoding> + Sync,
138        R,
139    >(
140        &'a mut self,
141        contract_name: &'a str,
142        caller: &'a Keypair,
143        constructor: &'a mut CreateBuilderPartial<E, Contract, Args, R>,
144    ) -> InstantiateBuilder<'a, E, Contract, Args, R, Self>
145    where
146        Self: Sized + BuilderClient<E>,
147    {
148        InstantiateBuilder::new(self, caller, contract_name, constructor)
149    }
150
151    /// Start building an upload call.
152    ///
153    /// # Example
154    ///
155    /// ```ignore
156    /// let contract = client
157    ///     .upload("flipper", &ink_e2e::alice())
158    ///     // Optional arguments
159    ///     .storage_deposit_limit(100)
160    ///     // Submit the call for on-chain execution.
161    ///     .submit()
162    ///     .await
163    ///     .expect("upload failed");
164    /// ```
165    fn upload<'a>(
166        &'a mut self,
167        contract_name: &'a str,
168        caller: &'a Keypair,
169    ) -> UploadBuilder<'a, E, Self>
170    where
171        Self: Sized + BuilderClient<E>,
172    {
173        UploadBuilder::new(self, contract_name, caller)
174    }
175
176    /// Start building a remove code call.
177    ///
178    /// # Example
179    ///
180    /// ```ignore
181    /// let contract = client
182    ///     .remove_code(&ink_e2e::alice(), code_hash)
183    ///     // Submit the call for on-chain execution.
184    ///     .submit()
185    ///     .await
186    ///     .expect("remove failed");
187    /// ```
188    fn remove_code<'a>(
189        &'a mut self,
190        caller: &'a Keypair,
191        code_hash: H256,
192    ) -> RemoveCodeBuilder<'a, E, Self>
193    where
194        Self: Sized + BuilderClient<E>,
195    {
196        RemoveCodeBuilder::new(self, caller, code_hash)
197    }
198
199    /// Start building a call using a builder pattern.
200    ///
201    /// # Example
202    ///
203    /// ```ignore
204    /// // Message method
205    /// let get = call_builder.get();
206    /// let get_res = client
207    ///    .call(&ink_e2e::bob(), &get)
208    ///     // Optional arguments
209    ///     // Send 100 units with the call.
210    ///     .value(100)
211    ///     // Add 10% margin to the gas limit
212    ///     .extra_gas_portion(10)
213    ///     .storage_deposit_limit(100)
214    ///     // Submit the call for on-chain execution.
215    ///     .submit()
216    ///     .await
217    ///     .expect("instantiate failed");
218    /// ```
219    fn call<
220        'a,
221        Args: Sync + AbiEncodeWith<Abi> + Clone,
222        RetType: Send + AbiDecodeWith<Abi>,
223        Abi: Sync + Clone,
224    >(
225        &'a mut self,
226        caller: &'a Keypair,
227        message: &'a CallBuilderFinal<E, Args, RetType, Abi>,
228    ) -> CallBuilder<'a, E, Args, RetType, Self, Abi>
229    where
230        Self: Sized + BuilderClient<E>,
231    {
232        CallBuilder::new(self, caller, message)
233    }
234}
235
236#[async_trait]
237pub trait BuilderClient<E: Environment>: ContractsBackend<E> {
238    /// Executes a bare `call` for the contract at `account_id`. This function does not
239    /// perform a dry-run, and the user is expected to provide the gas limit.
240    ///
241    /// Use it when you want to have a more precise control over submitting extrinsic.
242    ///
243    /// Returns when the transaction is included in a block. The return value
244    /// contains all events that are associated with this transaction.
245    async fn bare_call<
246        Args: Sync + AbiEncodeWith<Abi> + Clone,
247        RetType: Send + AbiDecodeWith<Abi>,
248        Abi: Sync + Clone,
249    >(
250        &mut self,
251        caller: &Keypair,
252        message: &CallBuilderFinal<E, Args, RetType, Abi>,
253        value: E::Balance,
254        gas_limit: Weight,
255        storage_deposit_limit: DepositLimit<E::Balance>,
256    ) -> Result<(Self::EventLog, Option<CallTrace>), Self::Error>
257    where
258        CallBuilderFinal<E, Args, RetType, Abi>: Clone;
259
260    /// Executes a dry-run `call`.
261    ///
262    /// Returns the result of the dry run, together with the decoded return value of the
263    /// invoked message.
264    async fn bare_call_dry_run<
265        Args: Sync + AbiEncodeWith<Abi> + Clone,
266        RetType: Send + AbiDecodeWith<Abi>,
267        Abi: Sync + Clone,
268    >(
269        &mut self,
270        caller: &Keypair,
271        message: &CallBuilderFinal<E, Args, RetType, Abi>,
272        value: E::Balance,
273        storage_deposit_limit: DepositLimit<E::Balance>,
274    ) -> Result<CallDryRunResult<E, RetType>, Self::Error>
275    where
276        CallBuilderFinal<E, Args, RetType, Abi>: Clone;
277
278    /// Uploads the contract call.
279    ///
280    /// This function extracts the binary of the contract for the specified contract.
281    ///
282    /// Calling this function multiple times should be idempotent, the contract is
283    /// newly instantiated each time using a unique salt. No existing contract
284    /// instance is reused!
285    async fn bare_upload(
286        &mut self,
287        contract_name: &str,
288        caller: &Keypair,
289        storage_deposit_limit: E::Balance,
290    ) -> Result<UploadResult<E, Self::EventLog>, Self::Error>;
291
292    /// Removes the code of the contract at `code_hash`.
293    async fn bare_remove_code(
294        &mut self,
295        caller: &Keypair,
296        code_hash: crate::H256,
297    ) -> Result<Self::EventLog, Self::Error>;
298
299    /// Bare instantiate call. This function does not perform a dry-run,
300    /// and user is expected to provide the gas limit.
301    ///
302    /// Use it when you want to have a more precise control over submitting extrinsic.
303    ///
304    /// The function subsequently uploads and instantiates an instance of the contract.
305    ///
306    /// This function extracts the metadata of the contract at the file path
307    /// `target/ink/$contract_name.contract`.
308    ///
309    /// Calling this function multiple times should be idempotent, the contract is
310    /// newly instantiated each time using a unique salt. No existing contract
311    /// instance is reused!
312    async fn bare_instantiate<
313        Contract: Clone,
314        Args: Send + Sync + AbiEncodeWith<ScaleEncoding> + Clone,
315        R,
316    >(
317        &mut self,
318        contract_name: &str,
319        caller: &Keypair,
320        constructor: &mut CreateBuilderPartial<E, Contract, Args, R>,
321        value: E::Balance,
322        gas_limit: Weight,
323        storage_deposit_limit: DepositLimit<E::Balance>,
324    ) -> Result<BareInstantiationResult<Self::EventLog>, Self::Error>;
325
326    /// Dry run contract instantiation.
327    async fn bare_instantiate_dry_run<
328        Contract: Clone,
329        Args: Send + Sync + AbiEncodeWith<ScaleEncoding> + Clone,
330        R,
331    >(
332        &mut self,
333        contract_name: &str,
334        caller: &Keypair,
335        constructor: &mut CreateBuilderPartial<E, Contract, Args, R>,
336        value: E::Balance,
337        storage_deposit_limit: DepositLimit<E::Balance>,
338    ) -> Result<InstantiateDryRunResult<E>, Self::Error>;
339
340    /// todo
341    async fn map_account(&mut self, caller: &Keypair) -> Result<(), Self::Error>;
342
343    /// todo
344    async fn map_account_dry_run(&mut self, caller: &Keypair) -> Result<(), Self::Error>;
345}