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}