ink_env/call/call_builder/mod.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
15mod call;
16mod delegate;
17
18pub use call::Call;
19pub use delegate::DelegateCall;
20
21use core::marker::PhantomData;
22
23use ink_primitives::{
24 abi::Sol,
25 Address,
26};
27
28use crate::{
29 call::{
30 utils::{
31 EmptyArgumentList,
32 ReturnType,
33 Set,
34 Unset,
35 },
36 Execution,
37 ExecutionInput,
38 },
39 types::Environment,
40};
41
42/// The final parameters to the cross-contract call.
43#[derive(Debug)]
44pub struct CallParams<E, CallType, Args, R, Abi>
45where
46 E: Environment,
47{
48 /// A marker to indicate which type of call to perform.
49 call_type: CallType,
50 /// The expected return type.
51 _return_type: ReturnType<R>,
52 /// The inputs to the execution which is a selector and encoded arguments.
53 exec_input: ExecutionInput<Args, Abi>,
54 /// `Environment` is used by `CallType` for correct types
55 _phantom: PhantomData<fn() -> E>,
56}
57
58impl<E, CallType, Args, R, Abi> CallParams<E, CallType, Args, R, Abi>
59where
60 E: Environment,
61{
62 /// Returns the execution input.
63 #[inline]
64 pub fn exec_input(&self) -> &ExecutionInput<Args, Abi> {
65 &self.exec_input
66 }
67}
68
69/// Returns a new [`CallBuilder`] to build up the parameters to a cross-contract call
70/// that uses the "default" ABI for calls for the ink! project.
71///
72/// # Note
73///
74/// The "default" ABI for calls is "ink", unless the ABI is set to "sol"
75/// in the ink! project's manifest file (i.e. `Cargo.toml`).
76///
77/// # Example
78///
79/// **Note:** The shown examples panic because there is currently no cross-calling
80/// support in the off-chain testing environment. However, this code
81/// should work fine in on-chain environments.
82///
83/// ## Example 1: No Return Value
84///
85/// The below example shows calling of a message of another contract that does
86/// not return any value back to its caller. The called function:
87///
88/// - has a selector equal to `0xDEADBEEF`
89/// - is provided with 5000 units of gas for its execution
90/// - is provided with 10 units of transferred value for the contract instance
91/// - receives the following arguments in order 1. an `i32` with value `42` 2. a `bool`
92/// with value `true` 3. an array of 32 `u8` with value `0x10`
93///
94/// ```should_panic
95/// # use ::ink_env::{
96/// # Environment,
97/// # DefaultEnvironment,
98/// # call::{build_call, Selector, ExecutionInput}
99/// # };
100/// # use ink_env::call::Call;
101/// # use ink_primitives::Address;
102///
103/// type AccountId = <DefaultEnvironment as Environment>::AccountId;
104/// # type Balance = <DefaultEnvironment as Environment>::Balance;
105/// build_call::<DefaultEnvironment>()
106/// .call(Address::from([0x42; 20]))
107/// .ref_time_limit(5000)
108/// .transferred_value(ink::U256::from(10))
109/// .exec_input(
110/// ExecutionInput::new(Selector::new([0xDE, 0xAD, 0xBE, 0xEF]))
111/// .push_arg(42u8)
112/// .push_arg(true)
113/// .push_arg(&[0x10u8; 32]),
114/// )
115/// .returns::<()>()
116/// .invoke();
117/// ```
118///
119/// ## Example 2: With Return Value
120///
121/// The below example shows calling of a message of another contract that does
122/// return a `i32` value back to its caller. The called function:
123///
124/// - has a selector equal to `0xDEADBEEF`
125/// - is provided with 5000 units of gas for its execution
126/// - is provided with 10 units of transferred value for the contract instance
127/// - receives the following arguments in order 1. an `i32` with value `42` 2. a `bool`
128/// with value `true` 3. an array of 32 `u8` with value `0x10`
129///
130/// ```should_panic
131/// # use ::ink_env::{
132/// # Environment,
133/// # DefaultEnvironment,
134/// # call::{build_call, Selector, ExecutionInput, Call},
135/// # };
136/// # type AccountId = <DefaultEnvironment as Environment>::AccountId;
137/// let my_return_value: i32 = build_call::<DefaultEnvironment>()
138/// .call_type(Call::new(ink::Address::from([0x42; 20])))
139/// .ref_time_limit(5000)
140/// .transferred_value(ink::U256::from(10))
141/// .exec_input(
142/// ExecutionInput::new(Selector::new([0xDE, 0xAD, 0xBE, 0xEF]))
143/// .push_arg(42u8)
144/// .push_arg(true)
145/// .push_arg(&[0x10u8; 32]),
146/// )
147/// .returns::<i32>()
148/// .invoke();
149/// ```
150///
151/// ## Example 3: Delegate call
152///
153/// **Note:** The shown example panics because there is currently no delegate calling
154/// support in the off-chain testing environment. However, this code
155/// should work fine in on-chain environments.
156///
157/// ```should_panic
158/// # use ::ink_env::{
159/// # Environment,
160/// # DefaultEnvironment,
161/// # call::{build_call, Selector, ExecutionInput, utils::ReturnType, DelegateCall},
162/// # };
163/// use ink::Address;
164/// # use ink_primitives::Clear;
165/// # type AccountId = <DefaultEnvironment as Environment>::AccountId;
166/// let my_return_value: i32 = build_call::<DefaultEnvironment>()
167/// .delegate(Address::zero())
168/// .exec_input(
169/// ExecutionInput::new(Selector::new([0xDE, 0xAD, 0xBE, 0xEF]))
170/// .push_arg(42u8)
171/// .push_arg(true)
172/// .push_arg(&[0x10u8; 32])
173/// )
174/// .returns::<i32>()
175/// .invoke();
176/// ```
177///
178/// # Handling `LangError`s
179///
180/// It is also important to note that there are certain types of errors which can happen
181/// during cross-contract calls which can be handled know as
182/// [`LangError`][`ink_primitives::LangError`].
183///
184/// If you want to handle these errors use the [`CallBuilder::try_invoke`] methods instead
185/// of the [`CallBuilder::invoke`] ones.
186///
187/// **Note:** The shown examples panic because there is currently no cross-calling
188/// support in the off-chain testing environment. However, this code
189/// should work fine in on-chain environments.
190///
191/// ## Example: Handling a `LangError`
192///
193/// ```should_panic
194/// # use ::ink_env::{
195/// # Environment,
196/// # DefaultEnvironment,
197/// # call::{build_call, Selector, ExecutionInput}
198/// # };
199/// # use ink_env::call::Call;
200/// # use ink_primitives::Address;
201///
202/// type AccountId = <DefaultEnvironment as Environment>::AccountId;
203/// # type Balance = <DefaultEnvironment as Environment>::Balance;
204/// let call_result = build_call::<DefaultEnvironment>()
205/// .call(Address::from([0x42; 20]))
206/// .ref_time_limit(5000)
207/// .transferred_value(ink::U256::from(10))
208/// .try_invoke()
209/// .expect("Got an error from the Contract's pallet.");
210///
211/// match call_result {
212/// Ok(_) => unimplemented!(),
213/// Err(e @ ink_primitives::LangError::CouldNotReadInput) => unimplemented!(),
214/// Err(_) => unimplemented!(),
215/// }
216/// ```
217#[allow(clippy::type_complexity)]
218pub fn build_call<E>() -> CallBuilder<
219 E,
220 Unset<Call>,
221 Unset<ExecutionInput<EmptyArgumentList<crate::DefaultAbi>, crate::DefaultAbi>>,
222 Unset<ReturnType<()>>,
223>
224where
225 E: Environment,
226{
227 CallBuilder {
228 call_type: Default::default(),
229 exec_input: Default::default(),
230 return_type: Default::default(),
231 _phantom: Default::default(),
232 }
233}
234
235/// Returns a new [`CallBuilder`] for the specified ABI to build up the parameters to a
236/// cross-contract call.
237/// See [`build_call`] for more details on usage.
238#[allow(clippy::type_complexity)]
239pub fn build_call_abi<E, Abi>() -> CallBuilder<
240 E,
241 Unset<Call>,
242 Unset<ExecutionInput<EmptyArgumentList<Abi>, Abi>>,
243 Unset<ReturnType<()>>,
244>
245where
246 E: Environment,
247{
248 CallBuilder {
249 call_type: Default::default(),
250 exec_input: Default::default(),
251 return_type: Default::default(),
252 _phantom: Default::default(),
253 }
254}
255
256/// Returns a new [`CallBuilder`] to build up the parameters to a cross-contract call
257/// that uses Solidity ABI Encoding.
258/// See [`build_call`] for more details on usage.
259#[allow(clippy::type_complexity)]
260pub fn build_call_solidity<E>() -> CallBuilder<
261 E,
262 Unset<Call>,
263 Unset<ExecutionInput<EmptyArgumentList<Sol>, Sol>>,
264 Unset<ReturnType<()>>,
265>
266where
267 E: Environment,
268{
269 CallBuilder {
270 call_type: Default::default(),
271 exec_input: Default::default(),
272 return_type: Default::default(),
273 _phantom: Default::default(),
274 }
275}
276
277/// Builds up a cross contract call.
278#[derive(Clone)]
279pub struct CallBuilder<E, CallType, Args, RetType>
280where
281 E: Environment,
282{
283 /// The current parameters that have been built up so far.
284 call_type: CallType,
285 exec_input: Args,
286 return_type: RetType,
287 _phantom: PhantomData<fn() -> E>, // todo possibly remove?
288}
289
290impl<E, Args, RetType, Abi> From<Execution<Args, RetType, Abi>>
291 for CallBuilder<
292 E,
293 Unset<Call>,
294 Set<ExecutionInput<Args, Abi>>,
295 Set<ReturnType<RetType>>,
296 >
297where
298 E: Environment,
299{
300 fn from(invoke: Execution<Args, RetType, Abi>) -> Self {
301 CallBuilder {
302 call_type: Default::default(),
303 exec_input: Set(invoke.input),
304 return_type: Set(invoke.output),
305 _phantom: Default::default(),
306 }
307 }
308}
309
310impl<E, CallType, Args, RetType> CallBuilder<E, Unset<CallType>, Args, RetType>
311where
312 E: Environment,
313{
314 /// The type of the call.
315 #[inline]
316 #[must_use]
317 pub fn call_type<NewCallType>(
318 self,
319 call_type: NewCallType,
320 ) -> CallBuilder<E, Set<NewCallType>, Args, RetType> {
321 CallBuilder {
322 call_type: Set(call_type),
323 exec_input: self.exec_input,
324 return_type: self.return_type,
325 _phantom: Default::default(),
326 }
327 }
328}
329
330impl<E, CallType, Args> CallBuilder<E, CallType, Args, Unset<ReturnType<()>>>
331where
332 E: Environment,
333{
334 /// Sets the type of the returned value upon the execution of the call.
335 ///
336 /// # Note
337 ///
338 /// Either use `.returns::<()>` to signal that the call does not return a value
339 /// or use `.returns::<T>` to signal that the call returns a value of type `T`.
340 #[inline]
341 pub fn returns<R>(self) -> CallBuilder<E, CallType, Args, Set<ReturnType<R>>> {
342 CallBuilder {
343 call_type: self.call_type,
344 exec_input: self.exec_input,
345 return_type: Set(Default::default()),
346 _phantom: Default::default(),
347 }
348 }
349}
350
351impl<E, CallType, RetType, Abi>
352 CallBuilder<E, CallType, Unset<ExecutionInput<EmptyArgumentList<Abi>, Abi>>, RetType>
353where
354 E: Environment,
355{
356 /// Sets the execution input to the given value.
357 pub fn exec_input<Args>(
358 self,
359 exec_input: ExecutionInput<Args, Abi>,
360 ) -> CallBuilder<E, CallType, Set<ExecutionInput<Args, Abi>>, RetType> {
361 CallBuilder {
362 call_type: self.call_type,
363 exec_input: Set(exec_input),
364 return_type: self.return_type,
365 _phantom: Default::default(),
366 }
367 }
368}
369
370impl<E, CallType, Args, RetType> CallBuilder<E, Unset<CallType>, Args, RetType>
371where
372 E: Environment,
373{
374 /// Prepares the `CallBuilder` for a cross-contract [`Call`] to the latest `call_v2`
375 /// host function.
376 pub fn call(self, callee: Address) -> CallBuilder<E, Set<Call>, Args, RetType> {
377 CallBuilder {
378 call_type: Set(Call::new(callee)),
379 exec_input: self.exec_input,
380 return_type: self.return_type,
381 _phantom: Default::default(),
382 }
383 }
384
385 /// Prepares the `CallBuilder` for a cross-contract [`DelegateCall`].
386 pub fn delegate(
387 self,
388 address: Address,
389 ) -> CallBuilder<E, Set<DelegateCall>, Args, RetType> {
390 CallBuilder {
391 // todo Generic `Set` can be removed
392 call_type: Set(DelegateCall::new(address)),
393 exec_input: self.exec_input,
394 return_type: self.return_type,
395 _phantom: Default::default(),
396 }
397 }
398}