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