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}