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}