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}