ink_env/call/call_builder/
call.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 crate::{
16    call::{
17        common::{
18            ReturnType,
19            Set,
20            Unset,
21        },
22        execution::EmptyArgumentList,
23        utils::DecodeMessageResult,
24        CallBuilder,
25        CallParams,
26        ExecutionInput,
27    },
28    types::{
29        Environment,
30        Gas,
31    },
32    Error,
33};
34use ink_primitives::{
35    reflect::{
36        AbiDecodeWith,
37        AbiEncodeWith,
38    },
39    H160,
40    U256,
41};
42use pallet_revive_uapi::CallFlags;
43
44/// The default call type for cross-contract calls, for calling into the latest `call`
45/// host function. This adds the additional weight limit parameter `proof_size_limit` as
46/// well as `storage_deposit_limit`.
47#[derive(Clone)]
48pub struct Call {
49    callee: H160,
50    ref_time_limit: u64,
51    proof_size_limit: u64,
52    storage_deposit_limit: Option<U256>,
53    transferred_value: U256,
54    call_flags: CallFlags,
55}
56
57impl Call {
58    /// Returns a clean builder for [`Call`].
59    pub fn new(callee: H160) -> Self {
60        Self {
61            callee,
62            ref_time_limit: u64::MAX,
63            proof_size_limit: u64::MAX,
64            storage_deposit_limit: None,
65            transferred_value: U256::zero(),
66            call_flags: CallFlags::empty(),
67        }
68    }
69}
70
71impl<E, Args, RetType> CallBuilder<E, Set<Call>, Args, RetType>
72where
73    E: Environment,
74{
75    /// Sets the `ref_time_limit` part of the weight limit for the current cross-contract
76    /// call.
77    ///
78    /// `ref_time` refers to the amount of computational time that can be
79    /// used for execution, in picoseconds. You can find more info
80    /// [here](https://use.ink/basics/gas).
81    pub fn ref_time_limit(self, ref_time_limit: Gas) -> Self {
82        let call_type = self.call_type.value();
83        CallBuilder {
84            call_type: Set(Call {
85                ref_time_limit,
86                ..call_type
87            }),
88            ..self
89        }
90    }
91
92    /// Sets the `proof_size_limit` part of the weight limit for the current
93    /// cross-contract call.
94    ///
95    /// `proof_size` refers to the amount of storage in bytes that a transaction
96    /// is allowed to read. You can find more info
97    /// [here](https://use.ink/basics/gas).
98    ///
99    /// **Note**
100    ///
101    /// This limit is only relevant for parachains, not for standalone chains which do not
102    /// require sending a Proof-of-validity to the relay chain.
103    pub fn proof_size_limit(self, proof_size_limit: Gas) -> Self {
104        let call_type = self.call_type.value();
105        CallBuilder {
106            call_type: Set(Call {
107                proof_size_limit,
108                ..call_type
109            }),
110            ..self
111        }
112    }
113
114    /// Sets the `storage_deposit_limit` for the current cross-contract call.
115    ///
116    /// The `storage_deposit_limit` specifies the amount of user funds that
117    /// can be charged for creating storage. You can find more info
118    /// [here](https://use.ink/basics/gas).
119    pub fn storage_deposit_limit(self, storage_deposit_limit: U256) -> Self {
120        let call_type = self.call_type.value();
121        CallBuilder {
122            call_type: Set(Call {
123                storage_deposit_limit: Some(storage_deposit_limit),
124                ..call_type
125            }),
126            ..self
127        }
128    }
129
130    /// Sets the `transferred_value` for the current cross-contract call.
131    ///
132    /// This value specifies the amount of user funds that are transferred
133    /// to the other contract with this call.
134    pub fn transferred_value(self, transferred_value: U256) -> Self {
135        let call_type = self.call_type.value();
136        CallBuilder {
137            call_type: Set(Call {
138                transferred_value,
139                ..call_type
140            }),
141            ..self
142        }
143    }
144
145    /// Sets the `call_flags` for the current cross-contract call.
146    ///
147    /// These flags are used to change the behavior of the contract call.
148    pub fn call_flags(self, call_flags: CallFlags) -> Self {
149        let call_type = self.call_type.value();
150        CallBuilder {
151            call_type: Set(Call {
152                call_flags,
153                ..call_type
154            }),
155            ..self
156        }
157    }
158}
159
160impl<E, Args, RetType, Abi>
161    CallBuilder<E, Set<Call>, Set<ExecutionInput<Args, Abi>>, Set<ReturnType<RetType>>>
162where
163    E: Environment,
164{
165    /// Finalizes the call builder to call a function.
166    pub fn params(self) -> CallParams<E, Call, Args, RetType, Abi> {
167        CallParams {
168            call_type: self.call_type.value(),
169            _return_type: Default::default(),
170            exec_input: self.exec_input.value(),
171            _phantom: self._phantom,
172        }
173    }
174}
175
176impl<E, RetType, Abi>
177    CallBuilder<
178        E,
179        Set<Call>,
180        Unset<ExecutionInput<EmptyArgumentList<Abi>, Abi>>,
181        Unset<RetType>,
182    >
183where
184    E: Environment,
185    Abi: Default,
186{
187    /// Finalizes the call builder to call a function.
188    pub fn params(self) -> CallParams<E, Call, EmptyArgumentList<Abi>, (), Abi> {
189        CallParams {
190            call_type: self.call_type.value(),
191            _return_type: Default::default(),
192            exec_input: Default::default(),
193            _phantom: self._phantom,
194        }
195    }
196}
197
198impl<E, Abi>
199    CallBuilder<
200        E,
201        Set<Call>,
202        Unset<ExecutionInput<EmptyArgumentList<Abi>, Abi>>,
203        Unset<ReturnType<()>>,
204    >
205where
206    E: Environment,
207    EmptyArgumentList<Abi>: AbiEncodeWith<Abi>,
208    (): AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
209    Abi: Default,
210{
211    /// Invokes the cross-chain function call.
212    ///
213    /// # Panics
214    ///
215    /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an
216    /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle
217    /// those use the [`try_invoke`][`CallBuilder::try_invoke`] method instead.
218    pub fn invoke(self) {
219        self.params().invoke()
220    }
221
222    /// Invokes the cross-chain function call.
223    ///
224    /// # Note
225    ///
226    /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner
227    /// [`ink::primitives::LangError`][`ink_primitives::LangError`], both of which can be
228    /// handled by the caller.
229    pub fn try_invoke(self) -> Result<ink_primitives::MessageResult<()>, Error> {
230        self.params().try_invoke()
231    }
232}
233
234impl<E, Args, R, Abi>
235    CallBuilder<E, Set<Call>, Set<ExecutionInput<Args, Abi>>, Set<ReturnType<R>>>
236where
237    E: Environment,
238    Args: AbiEncodeWith<Abi>,
239    R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
240    Abi: Default,
241{
242    /// Invokes the cross-chain function call and returns the result.
243    ///
244    /// # Panics
245    ///
246    /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an
247    /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle
248    /// those use the [`try_invoke`][`CallBuilder::try_invoke`] method instead.
249    pub fn invoke(self) -> R {
250        self.params().invoke()
251    }
252
253    /// Invokes the cross-chain function call and returns the result.
254    ///
255    /// # Note
256    ///
257    /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner
258    /// [`ink::primitives::LangError`][`ink_primitives::LangError`], both of which can be
259    /// handled by the caller.
260    pub fn try_invoke(self) -> Result<ink_primitives::MessageResult<R>, Error> {
261        self.params().try_invoke()
262    }
263}
264
265impl<E, Args, R, Abi> CallParams<E, Call, Args, R, Abi>
266where
267    E: Environment,
268{
269    /// Returns the contract address of the called contract instance.
270    #[inline]
271    pub fn callee(&self) -> &H160 {
272        &self.call_type.callee
273    }
274
275    /// Returns the chosen ref time limit for the called contract execution.
276    #[inline]
277    pub fn ref_time_limit(&self) -> u64 {
278        self.call_type.ref_time_limit
279    }
280
281    /// Returns the chosen proof size limit for the called contract execution.
282    #[inline]
283    pub fn proof_size_limit(&self) -> u64 {
284        self.call_type.proof_size_limit
285    }
286
287    /// Returns the chosen storage deposit limit for the called contract execution.
288    /// todo
289    #[inline]
290    pub fn storage_deposit_limit(&self) -> Option<U256> {
291        self.call_type.storage_deposit_limit
292    }
293
294    /// Returns the transferred value for the called contract.
295    #[inline]
296    pub fn transferred_value(&self) -> &U256 {
297        &self.call_type.transferred_value
298    }
299
300    /// Returns the call flags.
301    #[inline]
302    pub fn call_flags(&self) -> &CallFlags {
303        &self.call_type.call_flags
304    }
305}
306
307impl<E, Args, R, Abi> CallParams<E, Call, Args, R, Abi>
308where
309    E: Environment,
310    Args: AbiEncodeWith<Abi>,
311    R: AbiDecodeWith<Abi> + DecodeMessageResult<Abi>,
312{
313    /// Invokes the contract with the given built-up call parameters.
314    ///
315    /// Returns the result of the contract execution.
316    ///
317    /// # Panics
318    ///
319    /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an
320    /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle
321    /// those use the [`try_invoke`][`CallParams::try_invoke`] method instead.
322    pub fn invoke(&self) -> R {
323        crate::invoke_contract(self)
324            .unwrap_or_else(|env_error| {
325                panic!("Cross-contract call failed with {env_error:?}")
326            })
327            .unwrap_or_else(|lang_error| {
328                panic!("Cross-contract call failed with {lang_error:?}")
329            })
330    }
331
332    /// Invokes the contract with the given built-up call parameters.
333    ///
334    /// Returns the result of the contract execution.
335    ///
336    /// # Note
337    ///
338    /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner
339    /// [`ink::primitives::LangError`][`ink_primitives::LangError`], both of which can be
340    /// handled by the caller.
341    pub fn try_invoke(&self) -> Result<ink_primitives::MessageResult<R>, crate::Error> {
342        crate::invoke_contract(self)
343    }
344}