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    },
42    DepositLimit,
43};
44use jsonrpsee::core::async_trait;
45use pallet_revive::evm::CallTrace;
46use sp_weights::Weight;
47use subxt::dynamic::Value;
48
49/// Full E2E testing backend: combines general chain API and contract-specific operations.
50#[async_trait]
51pub trait E2EBackend<E: Environment = DefaultEnvironment>:
52    ChainBackend + BuilderClient<E>
53{
54}
55
56/// General chain operations useful in contract testing.
57#[async_trait]
58pub trait ChainBackend {
59    /// Account type.
60    type AccountId;
61    /// Balance type.
62    type Balance: Send + From<u32>;
63    /// Error type.
64    type Error;
65    /// Event log type.
66    type EventLog;
67
68    /// Generate a new account and fund it with the given `amount` of tokens from the
69    /// `origin`.
70    async fn create_and_fund_account(
71        &mut self,
72        origin: &Keypair,
73        amount: Self::Balance,
74    ) -> Keypair;
75
76    /// Returns the free balance of `account`.
77    async fn free_balance(
78        &mut self,
79        account: Self::AccountId,
80    ) -> Result<Self::Balance, Self::Error>;
81
82    /// Executes a runtime call `call_name` for the `pallet_name`.
83    /// The `call_data` is a `Vec<Value>`.
84    ///
85    /// Note:
86    /// - `pallet_name` must be in camel case, for example `Balances`.
87    /// - `call_name` must be snake case, for example `force_transfer`.
88    /// - `call_data` is a `Vec<subxt::dynamic::Value>` that holds a representation of
89    ///   some value.
90    ///
91    /// Returns when the transaction is included in a block. The return value contains all
92    /// events that are associated with this transaction.
93    ///
94    /// Since we might run node with an arbitrary runtime, this method inherently must
95    /// support dynamic calls.
96    async fn runtime_call<'a>(
97        &mut self,
98        origin: &Keypair,
99        pallet_name: &'a str,
100        call_name: &'a str,
101        call_data: Vec<Value>,
102    ) -> Result<Self::EventLog, Self::Error>;
103}
104
105/// Contract-specific operations.
106#[async_trait]
107pub trait ContractsBackend<E: Environment> {
108    /// Error type.
109    type Error;
110    /// Event log type.
111    type EventLog;
112
113    /// Start building an instantiate call using a builder pattern.
114    ///
115    /// # Example
116    ///
117    /// ```ignore
118    /// // Constructor method
119    /// let mut constructor = FlipperRef::new(false);
120    /// let contract = client
121    ///     .instantiate("flipper", &ink_e2e::alice(), &mut constructor)
122    ///     // Optional arguments
123    ///     // Send 100 units with the call.
124    ///     .value(100)
125    ///     // Add 10% margin to the gas limit
126    ///     .extra_gas_portion(10)
127    ///     .storage_deposit_limit(100)
128    ///     // Submit the call for on-chain execution.
129    ///     .submit()
130    ///     .await
131    ///     .expect("instantiate failed");
132    /// ```
133    fn instantiate<
134        'a,
135        Contract: Clone,
136        Args: Send + Clone + AbiEncodeWith<Abi> + Sync,
137        R,
138        Abi: Send + Sync + Clone,
139    >(
140        &'a mut self,
141        contract_name: &'a str,
142        caller: &'a Keypair,
143        constructor: &'a mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
144    ) -> InstantiateBuilder<'a, E, Contract, Args, R, Self, Abi>
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<Abi> + Clone,
315        R,
316        Abi: Send + Sync + Clone,
317    >(
318        &mut self,
319        contract_name: &str,
320        caller: &Keypair,
321        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
322        value: E::Balance,
323        gas_limit: Weight,
324        storage_deposit_limit: DepositLimit<E::Balance>,
325    ) -> Result<BareInstantiationResult<Self::EventLog>, Self::Error>;
326
327    /// Dry run contract instantiation.
328    async fn bare_instantiate_dry_run<
329        Contract: Clone,
330        Args: Send + Sync + AbiEncodeWith<Abi> + Clone,
331        R,
332        Abi: Send + Sync + Clone,
333    >(
334        &mut self,
335        contract_name: &str,
336        caller: &Keypair,
337        constructor: &mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
338        value: E::Balance,
339        storage_deposit_limit: DepositLimit<E::Balance>,
340    ) -> Result<InstantiateDryRunResult<E>, Self::Error>;
341
342    /// todo
343    async fn map_account(&mut self, caller: &Keypair) -> Result<(), Self::Error>;
344
345    /// todo
346    async fn map_account_dry_run(&mut self, caller: &Keypair) -> Result<(), Self::Error>;
347}