ink_e2e/
backend_calls.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 std::marker::PhantomData;
16
17use ink_env::{
18    Environment,
19    call::utils::{
20        DecodeMessageResult,
21        EncodeArgsWith,
22    },
23};
24use sp_weights::Weight;
25
26use super::{
27    InstantiateDryRunResult,
28    Keypair,
29};
30use crate::{
31    CallBuilderFinal,
32    CallDryRunResult,
33    CallResult,
34    ContractsBackend,
35    H256,
36    InstantiationResult,
37    UploadResult,
38    backend::BuilderClient,
39    builders::CreateBuilderPartial,
40};
41
42/// Allows to build an end-to-end call using a builder pattern.
43pub struct CallBuilder<'a, E, Args, RetType, B, Abi>
44where
45    E: Environment,
46    Args: EncodeArgsWith<Abi> + Clone,
47    RetType: Send + DecodeMessageResult<Abi>,
48    B: BuilderClient<E>,
49    Abi: Clone,
50{
51    client: &'a mut B,
52    caller: &'a Keypair,
53    message: &'a CallBuilderFinal<E, Args, RetType, Abi>,
54    value: E::Balance,
55    extra_gas_portion: Option<u64>,
56    gas_limit: Option<Weight>,
57    storage_deposit_limit: Option<E::Balance>,
58}
59
60impl<'a, E, Args, RetType, B, Abi> CallBuilder<'a, E, Args, RetType, B, Abi>
61where
62    E: Environment,
63    Args: Sync + EncodeArgsWith<Abi> + Clone,
64    RetType: Send + DecodeMessageResult<Abi>,
65    B: BuilderClient<E>,
66    Abi: Sync + Clone,
67{
68    /// Initialize a call builder with defaults values.
69    pub fn new(
70        client: &'a mut B,
71        caller: &'a Keypair,
72        message: &'a CallBuilderFinal<E, Args, RetType, Abi>,
73    ) -> CallBuilder<'a, E, Args, RetType, B, Abi>
74    where
75        E::Balance: From<u32>,
76    {
77        Self {
78            client,
79            caller,
80            message,
81            value: 0u32.into(),
82            extra_gas_portion: None,
83            gas_limit: None,
84            storage_deposit_limit: None,
85        }
86    }
87
88    /// Provide value with a call
89    pub fn value(&mut self, value: E::Balance) -> &mut Self {
90        self.value = value;
91        self
92    }
93
94    /// Increases the gas limit marginally by a specified percent.
95    /// Useful when the message's gas usage depends on the runtime state
96    /// and the dry run does not produce an accurate gas estimate.
97    ///
98    /// # Example
99    ///
100    /// With dry run gas estimate of `100` units and `5`% extra gas portion specified,
101    /// the set gas limit becomes `105` units
102    pub fn extra_gas_portion(&mut self, per_cent: u64) -> &mut Self {
103        if per_cent == 0 {
104            self.extra_gas_portion = None
105        } else {
106            self.extra_gas_portion = Some(per_cent)
107        }
108        self
109    }
110
111    /// Specifies the raw gas limit as part of the call.
112    ///
113    /// # Notes
114    ///
115    /// Overwrites any values specified for `extra_gas_portion`.
116    /// The gas estimate from the dry-run will be ignored.
117    pub fn gas_limit(&mut self, limit: Weight) -> &mut Self {
118        if limit == Weight::from_parts(0, 0) {
119            self.gas_limit = None
120        } else {
121            self.gas_limit = Some(limit)
122        }
123        self
124    }
125
126    /// Specify the max amount of funds that can be charged for storage.
127    pub fn storage_deposit_limit(
128        &mut self,
129        storage_deposit_limit: E::Balance,
130    ) -> &mut Self {
131        self.storage_deposit_limit = Some(storage_deposit_limit);
132        self
133    }
134
135    /// Submit the call for the on-chain execution.
136    ///
137    /// This will automatically run a dry-run call, and use `extra_gas_portion`
138    /// to add a margin to the gas limit.
139    pub async fn submit(
140        &mut self,
141    ) -> Result<CallResult<E, RetType, B::EventLog, Abi>, B::Error>
142    where
143        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
144    {
145        let _map = B::map_account(self.client, self.caller).await; // todo will fail if instantiation happened before
146
147        let dry_run = B::bare_call_dry_run(
148            self.client,
149            self.caller,
150            self.message,
151            self.value,
152            self.storage_deposit_limit,
153        )
154        .await?;
155
156        let gas_limit = if let Some(limit) = self.gas_limit {
157            limit
158        } else {
159            let gas_required = dry_run.exec_result.gas_required;
160            let proof_size = gas_required.proof_size();
161            let ref_time = gas_required.ref_time();
162            calculate_weight(proof_size, ref_time, self.extra_gas_portion)
163        };
164
165        let (events, trace) = B::bare_call(
166            self.client,
167            self.caller,
168            self.message,
169            self.value,
170            gas_limit,
171            dry_run.exec_result.storage_deposit.charge_or_zero(),
172        )
173        .await?;
174
175        Ok(CallResult {
176            dry_run,
177            events,
178            trace,
179        })
180    }
181
182    /// Dry run the call.
183    pub async fn dry_run(&mut self) -> Result<CallDryRunResult<E, RetType, Abi>, B::Error>
184    where
185        CallBuilderFinal<E, Args, RetType, Abi>: Clone,
186    {
187        B::bare_call_dry_run(
188            self.client,
189            self.caller,
190            self.message,
191            self.value,
192            self.storage_deposit_limit,
193        )
194        .await
195    }
196}
197
198/// Allows to build an end-to-end instantiation call using a builder pattern.
199pub struct InstantiateBuilder<'a, E, Contract, Args, R, B, Abi>
200where
201    E: Environment,
202    Args: EncodeArgsWith<Abi> + Clone,
203    Contract: Clone,
204    B: ContractsBackend<E>,
205{
206    client: &'a mut B,
207    caller: &'a Keypair,
208    contract_name: &'a str,
209    constructor: &'a mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
210    value: E::Balance,
211    extra_gas_portion: Option<u64>,
212    gas_limit: Option<Weight>,
213    storage_deposit_limit: Option<E::Balance>,
214}
215
216impl<'a, E, Contract, Args, R, B, Abi>
217    InstantiateBuilder<'a, E, Contract, Args, R, B, Abi>
218where
219    E: Environment,
220    Args: EncodeArgsWith<Abi> + Clone + Send + Sync,
221    Contract: Clone,
222    B: BuilderClient<E>,
223    Abi: Send + Sync + Clone,
224{
225    /// Initialize a call builder with essential values.
226    pub fn new(
227        client: &'a mut B,
228        caller: &'a Keypair,
229        contract_name: &'a str,
230        constructor: &'a mut CreateBuilderPartial<E, Contract, Args, R, Abi>,
231    ) -> InstantiateBuilder<'a, E, Contract, Args, R, B, Abi>
232    where
233        E::Balance: From<u32>,
234    {
235        Self {
236            client,
237            caller,
238            contract_name,
239            constructor,
240            value: 0u32.into(),
241            extra_gas_portion: None,
242            gas_limit: None,
243            storage_deposit_limit: None,
244        }
245    }
246
247    /// Provide value with a call
248    pub fn value(&mut self, value: E::Balance) -> &mut Self {
249        self.value = value;
250        self
251    }
252
253    /// Increases the gas limit marginally by a specified percent.
254    /// Useful when the message's gas usage depends on the runtime state
255    /// and the dry run does not produce an accurate gas estimate.
256    ///
257    /// # Example
258    ///
259    /// With dry run gas estimate of `100` units and `5`% extra gas portion specified,
260    /// the set gas limit becomes `105` units
261    pub fn extra_gas_portion(&mut self, per_cent: u64) -> &mut Self {
262        if per_cent == 0 {
263            self.extra_gas_portion = None
264        } else {
265            self.extra_gas_portion = Some(per_cent)
266        }
267        self
268    }
269
270    /// Specifies the raw gas limit as part of the call.
271    ///
272    /// # Notes
273    ///
274    /// Overwrites any values specified for `extra_gas_portion`.
275    /// The gas estimate from dry-run will be ignored.
276    pub fn gas_limit(&mut self, limit: Weight) -> &mut Self {
277        if limit == Weight::from_parts(0, 0) {
278            self.gas_limit = None
279        } else {
280            self.gas_limit = Some(limit)
281        }
282        self
283    }
284
285    /// Specify the max amount of funds that can be charged for storage.
286    ///
287    /// *Important*: `None` means charging the maximum!
288    pub fn storage_deposit_limit(
289        &mut self,
290        storage_deposit_limit: Option<E::Balance>,
291    ) -> &mut Self {
292        self.storage_deposit_limit = storage_deposit_limit;
293        self
294    }
295
296    /// Submit the instantiate call for the on-chain execution.
297    ///
298    /// This will automatically run a dry-run call, and use `extra_gas_portion`
299    /// to add a margin to the gas limit.
300    pub async fn submit(
301        &mut self,
302    ) -> Result<InstantiationResult<E, B::EventLog, Abi>, B::Error> {
303        // we have to make sure the account was mapped
304        let _map = B::map_account(self.client, self.caller).await; // todo will fail if instantiation happened before
305
306        let dry_run = B::bare_instantiate_dry_run(
307            self.client,
308            self.contract_name,
309            self.caller,
310            self.constructor,
311            self.value,
312            self.storage_deposit_limit.clone(),
313        )
314        .await?;
315
316        let gas_limit = if let Some(limit) = self.gas_limit {
317            limit
318        } else {
319            let gas_required = dry_run.contract_result.gas_required;
320            let proof_size = gas_required.proof_size();
321            let ref_time = gas_required.ref_time();
322            calculate_weight(proof_size, ref_time, self.extra_gas_portion)
323        };
324
325        let instantiate_result = B::bare_instantiate(
326            self.client,
327            B::load_code(self.client, self.contract_name),
328            self.caller,
329            self.constructor,
330            self.value,
331            gas_limit,
332            dry_run.contract_result.storage_deposit.charge_or_zero(),
333        )
334        .await?;
335
336        Ok(InstantiationResult {
337            addr: instantiate_result.addr,
338            account_id: instantiate_result.account_id,
339            dry_run,
340            events: instantiate_result.events,
341            trace: instantiate_result.trace,
342            code_hash: instantiate_result.code_hash,
343        })
344    }
345
346    /// Dry run the instantiate call.
347    pub async fn dry_run(&mut self) -> Result<InstantiateDryRunResult<E, Abi>, B::Error> {
348        B::bare_instantiate_dry_run(
349            self.client,
350            self.contract_name,
351            self.caller,
352            self.constructor,
353            self.value,
354            self.storage_deposit_limit.clone(),
355        )
356        .await
357    }
358}
359
360/// Allows to build an end-to-end upload call using a builder pattern.
361pub struct UploadBuilder<'a, E, B>
362where
363    E: Environment,
364    B: BuilderClient<E>,
365{
366    client: &'a mut B,
367    contract_name: &'a str,
368    caller: &'a Keypair,
369    storage_deposit_limit: Option<E::Balance>,
370}
371
372impl<'a, E, B> UploadBuilder<'a, E, B>
373where
374    E: Environment,
375    B: BuilderClient<E>,
376{
377    /// Initialize an upload builder with essential values.
378    pub fn new(client: &'a mut B, contract_name: &'a str, caller: &'a Keypair) -> Self {
379        Self {
380            client,
381            contract_name,
382            caller,
383            storage_deposit_limit: None,
384        }
385    }
386
387    /// Specify the max amount of funds that can be charged for storage.
388    pub fn storage_deposit_limit(
389        &mut self,
390        storage_deposit_limit: Option<E::Balance>,
391    ) -> &mut Self {
392        self.storage_deposit_limit = storage_deposit_limit;
393        self
394    }
395
396    /// Execute the upload.
397    pub async fn submit(&mut self) -> Result<UploadResult<E, B::EventLog>, B::Error> {
398        B::bare_upload(
399            self.client,
400            self.contract_name,
401            self.caller,
402            self.storage_deposit_limit,
403        )
404        .await
405    }
406}
407
408/// Allows to build an end-to-end remove code call using a builder pattern.
409pub struct RemoveCodeBuilder<'a, E, B>
410where
411    E: Environment,
412    B: BuilderClient<E>,
413{
414    client: &'a mut B,
415    caller: &'a Keypair,
416    code_hash: crate::H256,
417    _phantom: PhantomData<fn() -> E>,
418}
419
420impl<'a, E, B> RemoveCodeBuilder<'a, E, B>
421where
422    E: Environment,
423    B: BuilderClient<E>,
424{
425    /// Initialize a remove code builder with essential values.
426    pub fn new(client: &'a mut B, caller: &'a Keypair, code_hash: H256) -> Self {
427        Self {
428            client,
429            caller,
430            code_hash,
431            _phantom: Default::default(),
432        }
433    }
434
435    /// Submit the remove code extrinsic.
436    pub async fn submit(&mut self) -> Result<B::EventLog, B::Error> {
437        B::bare_remove_code(self.client, self.caller, self.code_hash).await
438    }
439}
440
441fn calculate_weight(
442    mut proof_size: u64,
443    mut ref_time: u64,
444    portion: Option<u64>,
445) -> Weight {
446    if let Some(m) = portion {
447        ref_time += ref_time / 100 * m;
448        proof_size += proof_size / 100 * m;
449    }
450    Weight::from_parts(ref_time, proof_size)
451}