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