ink_env/call/create_builder.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 core::marker::PhantomData;
16
17use ink_primitives::{
18 reflect::{
19 AbiEncodeWith,
20 ScaleEncoding,
21 },
22 Address,
23 H256,
24 U256,
25};
26
27#[cfg(feature = "unstable-hostfn")]
28use crate::Error;
29use crate::{
30 call::{
31 utils::{
32 EmptyArgumentList,
33 ReturnType,
34 Set,
35 Unset,
36 },
37 ExecutionInput,
38 Selector,
39 },
40 types::Environment,
41 ContractEnv,
42};
43
44pub mod state {
45 //! Type states that tell what state of a instantiation argument has not
46 //! yet been set properly for a valid construction.
47}
48
49/// Contracts that can be constructed from an `AccountId`.
50///
51/// # Note
52///
53/// This is needed because of conflicting implementations of `From<T> for T`
54/// in the generated code of `ink`.
55///
56/// But it is possible to use `From<AccountId> for T` with [`crate::AccountIdGuard`]
57/// bound.
58pub trait FromAddr {
59 /// Creates the contract instance from the account ID of the already instantiated
60 /// contract.
61 fn from_addr(addr: Address) -> Self;
62}
63
64/// Represents any type that can be returned from an `ink!` constructor. The following
65/// contract implements the four different return type signatures implementing this trait:
66///
67/// - `Self`
68/// - `Result<Self, Error>`
69/// - `Contract`
70/// - `Result<Contract, Error>`
71///
72/// ```rust
73/// #[ink::contract]
74/// mod contract {
75/// #[ink(storage)]
76/// pub struct Contract {}
77///
78/// #[derive(Debug, PartialEq, Eq)]
79/// #[ink::scale_derive(Encode, Decode, TypeInfo)]
80/// pub enum Error {
81/// Foo,
82/// }
83///
84/// impl Contract {
85/// #[ink(constructor)]
86/// pub fn new_self() -> Self {
87/// Self {}
88/// }
89///
90/// #[ink(constructor)]
91/// pub fn new_storage_name() -> Contract {
92/// Contract {}
93/// }
94///
95/// #[ink(constructor)]
96/// pub fn new_result_self() -> Result<Self, Error> {
97/// Ok(Self {})
98/// }
99///
100/// #[ink(constructor)]
101/// pub fn new_result_storage_name() -> Result<Contract, Error> {
102/// Ok(Contract {})
103/// }
104///
105/// #[ink(message)]
106/// pub fn message(&self) {}
107/// }
108/// }
109/// ```
110///
111/// These constructor return signatures are then used by the `ContractRef` codegen for the
112/// [`CreateBuilder::returns`] type parameter.
113pub trait ConstructorReturnType<C> {
114 /// Is `true` if `Self` is `Result<C, E>`.
115 const IS_RESULT: bool = false;
116
117 /// The actual return type of the constructor.
118 /// - If a constructor returns `Self`, then `Output = Self`
119 /// - If a constructor returns a `Result<Self, E>`, then `Output = Result<Self, E>`
120 type Output;
121
122 /// The error type of the constructor return type.
123 type Error: scale::Decode;
124
125 /// Construct a success value of the `Output` type.
126 fn ok(value: C) -> Self::Output;
127
128 /// Construct an error value of the `Output` type.
129 ///
130 /// `Result` implementations should return `Some(Err(err))`, otherwise default to
131 /// `None`.
132 fn err(_err: Self::Error) -> Option<Self::Output> {
133 None
134 }
135}
136
137/// Blanket implementation for `ContractRef` types, generated for cross-contract calls.
138///
139/// In the context of a `ContractRef` inherent, `Self` from a constructor return
140/// type will become the type of the `ContractRef`'s type.
141impl<C> ConstructorReturnType<C> for C
142where
143 C: ContractEnv + FromAddr,
144{
145 type Output = C;
146 type Error = ();
147
148 fn ok(value: C) -> Self::Output {
149 value
150 }
151}
152
153/// Blanket implementation for a `Result<Self>` return type. `Self` in the context
154/// of a `ContractRef` inherent becomes the `ContractRef`s type.
155impl<C, E> ConstructorReturnType<C> for core::result::Result<C, E>
156where
157 C: ContractEnv + FromAddr,
158 E: scale::Decode,
159{
160 const IS_RESULT: bool = true;
161
162 type Output = core::result::Result<C, E>;
163 type Error = E;
164
165 fn ok(value: C) -> Self::Output {
166 Ok(value)
167 }
168
169 fn err(err: Self::Error) -> Option<Self::Output> {
170 Some(Err(err))
171 }
172}
173
174/// Defines the limit params for the new `ext::instantiate` host function.
175/// todo: rename
176#[derive(Clone, Debug)]
177pub struct LimitParamsV2 {
178 ref_time_limit: u64,
179 proof_size_limit: u64,
180 storage_deposit_limit: Option<U256>,
181}
182
183/// Builds up contract instantiations.
184#[derive(Debug)]
185pub struct CreateParams<E, ContractRef, Limits, Args, R> {
186 /// The code hash of the created contract.
187 code_hash: H256,
188 /// Parameters for weight and storage limits, differs for versions of the instantiate
189 /// host function.
190 limits: Limits,
191 /// The endowment for the instantiated contract.
192 /// todo: is this correct? or is the value here `U256`?
193 endowment: U256,
194 /// The input data for the instantiation.
195 exec_input: ExecutionInput<Args, ScaleEncoding>,
196 /// The salt for determining the hash for the contract account ID.
197 salt_bytes: Option<[u8; 32]>,
198 /// The return type of the target contract's constructor method.
199 _return_type: ReturnType<R>,
200 /// The type of the reference to the contract returned from the constructor.
201 _phantom: PhantomData<fn() -> (E, ContractRef)>,
202}
203
204impl<E, ContractRef, Limits, Args, R> CreateParams<E, ContractRef, Limits, Args, R>
205where
206 E: Environment,
207{
208 /// The code hash of the contract.
209 #[inline]
210 pub fn code_hash(&self) -> &H256 {
211 &self.code_hash
212 }
213
214 /// The endowment for the instantiated contract.
215 #[inline]
216 pub fn endowment(&self) -> &U256 {
217 &self.endowment
218 }
219
220 /// The raw encoded input data.
221 #[inline]
222 pub fn exec_input(&self) -> &ExecutionInput<Args, ScaleEncoding> {
223 &self.exec_input
224 }
225
226 /// Modify the selector.
227 ///
228 /// Useful when using the [`CreateParams`] generated as part of the
229 /// `ContractRef`, but using a custom selector.
230 pub fn update_selector(&mut self, selector: Selector) {
231 self.exec_input.update_selector(selector)
232 }
233}
234
235impl<E, ContractRef, Args, R> CreateParams<E, ContractRef, LimitParamsV2, Args, R>
236where
237 E: Environment,
238{
239 /// Gets the `ref_time_limit` part of the weight limit for the contract instantiation.
240 #[inline]
241 pub fn ref_time_limit(&self) -> u64 {
242 self.limits.ref_time_limit
243 }
244
245 /// Gets the `proof_size_limit` part of the weight limit for the contract
246 /// instantiation.
247 #[inline]
248 pub fn proof_size_limit(&self) -> u64 {
249 self.limits.proof_size_limit
250 }
251
252 /// Gets the `storage_deposit_limit` for the contract instantiation.
253 #[inline]
254 pub fn storage_deposit_limit(&self) -> Option<&U256> {
255 self.limits.storage_deposit_limit.as_ref()
256 }
257}
258
259impl<E, ContractRef, Limits, Args, R> CreateParams<E, ContractRef, Limits, Args, R>
260where
261 E: Environment,
262{
263 /// The salt for determining the hash for the contract account ID.
264 #[inline]
265 pub fn salt_bytes(&self) -> &Option<[u8; 32]> {
266 &self.salt_bytes
267 }
268}
269
270impl<E, ContractRef, Args, R> CreateParams<E, ContractRef, LimitParamsV2, Args, R>
271where
272 E: Environment,
273 ContractRef: FromAddr + crate::ContractReverseReference,
274 <ContractRef as crate::ContractReverseReference>::Type:
275 crate::reflect::ContractConstructorDecoder,
276 <ContractRef as crate::ContractReverseReference>::Type:
277 crate::reflect::ContractMessageDecoder,
278 Args: AbiEncodeWith<ScaleEncoding>,
279 R: ConstructorReturnType<ContractRef>,
280{
281 /// todo
282 /// Instantiates the contract and returns its account ID back to the caller.
283 ///
284 /// # Panics
285 ///
286 /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an
287 /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle
288 /// those use the [`try_instantiate`][`CreateParams::try_instantiate`] method
289 /// instead.
290 #[inline]
291 #[cfg(feature = "unstable-hostfn")]
292 pub fn instantiate(&self) -> <R as ConstructorReturnType<ContractRef>>::Output {
293 crate::instantiate_contract(self)
294 .unwrap_or_else(|env_error| {
295 panic!("Cross-contract instantiation failed with {env_error:?}")
296 })
297 .unwrap_or_else(|lang_error| {
298 panic!("Received a `LangError` while instantiating: {lang_error:?}")
299 })
300 }
301
302 /// Instantiates the contract and returns its account ID back to the caller.
303 ///
304 /// # Note
305 ///
306 /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner
307 /// [`ink::primitives::LangError`][`ink_primitives::LangError`], both of which can be
308 /// handled by the caller.
309 #[inline]
310 #[cfg(feature = "unstable-hostfn")]
311 pub fn try_instantiate(
312 &self,
313 ) -> Result<
314 ink_primitives::ConstructorResult<
315 <R as ConstructorReturnType<ContractRef>>::Output,
316 >,
317 Error,
318 > {
319 crate::instantiate_contract(self)
320 }
321}
322
323/// Builds up contract instantiations.
324#[derive(Clone)]
325pub struct CreateBuilder<E, ContractRef, Limits, Args, RetType>
326where
327 E: Environment,
328{
329 code_hash: H256,
330 limits: Limits,
331 endowment: U256,
332 exec_input: Args,
333 salt: Option<[u8; 32]>,
334 return_type: RetType,
335 _phantom: PhantomData<fn() -> (E, ContractRef)>,
336}
337
338/// Returns a new [`CreateBuilder`] to build up the parameters to a cross-contract
339/// instantiation.
340///
341/// # Example
342///
343/// **Note:** The shown examples panic because there is currently no cross-calling
344/// support in the off-chain testing environment. However, this code
345/// should work fine in on-chain environments.
346///
347/// ## Example 1: Returns Address of Instantiated Contract
348///
349/// The below example shows instantiation of contract of type `MyContract`.
350///
351/// The used constructor:
352///
353/// - has a selector equal to `0xDEADBEEF`
354/// - is provided with 4000 units of gas for its execution
355/// - is provided with 25 units of transferred value for the new contract instance
356/// - receives the following arguments in order 1. an `i32` with value `42` 2. a `bool`
357/// with value `true` 3. an array of 32 `u8` with value `0x10`
358///
359/// ```should_panic
360/// # use ::ink_env::{
361/// # Environment,
362/// # DefaultEnvironment,
363/// # call::{build_create, Selector, ExecutionInput, FromAddr}
364/// # };
365/// # type Hash = <DefaultEnvironment as Environment>::Hash;
366/// #
367/// # #[ink::contract]
368/// # pub mod contract {
369/// # #[ink(storage)]
370/// # pub struct MyContract {}
371/// #
372/// # impl MyContract {
373/// # #[ink(constructor)]
374/// # pub fn my_constructor() -> Self { Self {} }
375/// #
376/// # #[ink(message)]
377/// # pub fn message(&self) {}
378/// # }
379/// # }
380/// # use contract::MyContractRef;
381/// let my_contract: MyContractRef = build_create::<MyContractRef>()
382/// .code_hash(ink::H256::from([0x42; 32]))
383/// .endowment(25.into())
384/// .exec_input(
385/// ExecutionInput::new(Selector::new(ink::selector_bytes!("my_constructor")))
386/// .push_arg(42)
387/// .push_arg(true)
388/// .push_arg(&[0x10u8; 32]),
389/// )
390/// .salt_bytes(Some([1u8; 32]))
391/// .returns::<MyContractRef>()
392/// .instantiate();
393/// ```
394///
395/// ## Example 2: Handles Result from Fallible Constructor
396///
397/// ```should_panic
398/// # use ::ink_env::{
399/// # Environment,
400/// # DefaultEnvironment,
401/// # call::{build_create, Selector, ExecutionInput, FromAddr}
402/// # };
403/// # type Hash = <DefaultEnvironment as Environment>::Hash;
404/// #
405/// # #[ink::contract]
406/// # pub mod contract {
407/// # #[derive(scale::Encode, scale::Decode, Debug)]
408/// # #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
409/// # pub struct ConstructorError;
410/// #
411/// # #[ink(storage)]
412/// # pub struct MyContract {}
413/// #
414/// # impl MyContract {
415/// # #[ink(constructor)]
416/// # pub fn my_constructor() -> Result<Self, ConstructorError> {
417/// # Ok(Self {})
418/// # }
419/// #
420/// # #[ink(message)]
421/// # pub fn message(&self) {}
422/// # }
423/// # }
424/// # use contract::{MyContractRef, ConstructorError};
425/// let my_contract: MyContractRef = build_create::<MyContractRef>()
426/// .code_hash(ink::H256::from([0x42; 32]))
427/// .endowment(25.into())
428/// .exec_input(
429/// ExecutionInput::new(Selector::new(ink::selector_bytes!("my_constructor")))
430/// .push_arg(42)
431/// .push_arg(true)
432/// .push_arg(&[0x10u8; 32]),
433/// )
434/// .salt_bytes(Some([1u8; 32]))
435/// .returns::<Result<MyContractRef, ConstructorError>>()
436/// .instantiate()
437/// .expect("Constructor should have executed successfully.");
438/// ```
439#[allow(clippy::type_complexity)]
440pub fn build_create<ContractRef>() -> CreateBuilder<
441 <ContractRef as ContractEnv>::Env,
442 ContractRef,
443 Set<LimitParamsV2>,
444 Unset<ExecutionInput<EmptyArgumentList<ScaleEncoding>, ScaleEncoding>>,
445 Unset<ReturnType<()>>,
446>
447where
448 ContractRef: ContractEnv,
449{
450 CreateBuilder {
451 code_hash: Default::default(),
452 limits: Set(LimitParamsV2 {
453 ref_time_limit: u64::MAX,
454 proof_size_limit: u64::MAX,
455 storage_deposit_limit: None,
456 }),
457 endowment: Default::default(),
458 exec_input: Default::default(),
459 salt: Default::default(),
460 return_type: Default::default(),
461 _phantom: Default::default(),
462 }
463}
464
465impl<E, ContractRef, Limits, Args, RetType>
466 CreateBuilder<E, ContractRef, Limits, Args, RetType>
467where
468 E: Environment,
469{
470 /// Sets the used code hash for the contract instantiation.
471 #[inline]
472 pub fn code_hash(
473 self,
474 code_hash: H256,
475 ) -> CreateBuilder<E, ContractRef, Limits, Args, RetType> {
476 CreateBuilder {
477 code_hash,
478 limits: self.limits,
479 endowment: self.endowment,
480 exec_input: self.exec_input,
481 salt: self.salt,
482 return_type: self.return_type,
483 _phantom: Default::default(),
484 }
485 }
486}
487
488impl<E, ContractRef, Args, RetType>
489 CreateBuilder<E, ContractRef, Set<LimitParamsV2>, Args, RetType>
490where
491 E: Environment,
492{
493 /// Sets the `ref_time_limit` part of the weight limit for the contract instantiation.
494 #[inline]
495 pub fn ref_time_limit(self, ref_time_limit: u64) -> Self {
496 CreateBuilder {
497 limits: Set(LimitParamsV2 {
498 ref_time_limit,
499 ..self.limits.value()
500 }),
501 ..self
502 }
503 }
504
505 /// Sets the `proof_size_limit` part of the weight limit for the contract
506 /// instantiation.
507 #[inline]
508 pub fn proof_size_limit(self, proof_size_limit: u64) -> Self {
509 CreateBuilder {
510 limits: Set(LimitParamsV2 {
511 proof_size_limit,
512 ..self.limits.value()
513 }),
514 ..self
515 }
516 }
517
518 /// Sets the `storage_deposit_limit` for the contract instantiation.
519 #[inline]
520 pub fn storage_deposit_limit(self, storage_deposit_limit: U256) -> Self {
521 CreateBuilder {
522 limits: Set(LimitParamsV2 {
523 storage_deposit_limit: Some(storage_deposit_limit),
524 ..self.limits.value()
525 }),
526 ..self
527 }
528 }
529}
530
531impl<E, ContractRef, Limits, Args, RetType>
532 CreateBuilder<E, ContractRef, Limits, Args, RetType>
533where
534 E: Environment,
535{
536 /// Sets the value transferred upon the execution of the call.
537 #[inline]
538 pub fn endowment(
539 self,
540 endowment: U256,
541 ) -> CreateBuilder<E, ContractRef, Limits, Args, RetType> {
542 CreateBuilder {
543 code_hash: self.code_hash,
544 limits: self.limits,
545 endowment,
546 exec_input: self.exec_input,
547 salt: self.salt,
548 return_type: self.return_type,
549 _phantom: Default::default(),
550 }
551 }
552}
553
554impl<E, ContractRef, Limits, RetType, Abi>
555 CreateBuilder<
556 E,
557 ContractRef,
558 Limits,
559 Unset<ExecutionInput<EmptyArgumentList<Abi>, Abi>>,
560 RetType,
561 >
562where
563 E: Environment,
564{
565 /// Sets the value transferred upon the execution of the call.
566 #[inline]
567 pub fn exec_input<Args>(
568 self,
569 exec_input: ExecutionInput<Args, Abi>,
570 ) -> CreateBuilder<E, ContractRef, Limits, Set<ExecutionInput<Args, Abi>>, RetType>
571 {
572 CreateBuilder {
573 code_hash: self.code_hash,
574 limits: self.limits,
575 endowment: self.endowment,
576 exec_input: Set(exec_input),
577 salt: self.salt,
578 return_type: self.return_type,
579 _phantom: Default::default(),
580 }
581 }
582}
583
584impl<E, ContractRef, Limits, Args, RetType>
585 CreateBuilder<E, ContractRef, Limits, Args, RetType>
586where
587 E: Environment,
588{
589 /// Sets the salt used for the execution of the call.
590 #[inline]
591 pub fn salt_bytes(
592 self,
593 salt: Option<[u8; 32]>,
594 ) -> CreateBuilder<E, ContractRef, Limits, Args, RetType> {
595 CreateBuilder {
596 code_hash: self.code_hash,
597 limits: self.limits,
598 endowment: self.endowment,
599 exec_input: self.exec_input,
600 salt,
601 return_type: self.return_type,
602 _phantom: Default::default(),
603 }
604 }
605}
606
607impl<E, ContractRef, Limits, Args>
608 CreateBuilder<E, ContractRef, Limits, Args, Unset<ReturnType<()>>>
609where
610 E: Environment,
611{
612 /// Sets the type of the returned value upon the execution of the constructor.
613 ///
614 /// # Note
615 ///
616 /// Constructors are not able to return arbitrary values. Instead, a successful call
617 /// to a constructor returns the address at which the contract was instantiated.
618 ///
619 /// Therefore this must always be a reference (i.e. `ContractRef`) to the contract
620 /// you're trying to instantiate.
621 #[inline]
622 pub fn returns<R>(
623 self,
624 ) -> CreateBuilder<E, ContractRef, Limits, Args, Set<ReturnType<R>>>
625 where
626 ContractRef: FromAddr,
627 R: ConstructorReturnType<ContractRef>,
628 {
629 CreateBuilder {
630 code_hash: self.code_hash,
631 limits: self.limits,
632 endowment: self.endowment,
633 exec_input: self.exec_input,
634 salt: self.salt,
635 return_type: Set(Default::default()),
636 _phantom: Default::default(),
637 }
638 }
639}
640
641impl<E, ContractRef, Limits, Args, RetType>
642 CreateBuilder<
643 E,
644 ContractRef,
645 Set<Limits>,
646 Set<ExecutionInput<Args, ScaleEncoding>>,
647 Set<ReturnType<RetType>>,
648 >
649where
650 E: Environment,
651{
652 /// Finalizes the `CreateBuilder`, allowing it to instantiate a contract.
653 #[inline]
654 pub fn params(self) -> CreateParams<E, ContractRef, Limits, Args, RetType> {
655 CreateParams {
656 code_hash: self.code_hash,
657 limits: self.limits.value(),
658 endowment: self.endowment,
659 exec_input: self.exec_input.value(),
660 salt_bytes: self.salt,
661 _return_type: Default::default(),
662 _phantom: Default::default(),
663 }
664 }
665}
666
667impl<E, ContractRef, Args, RetType>
668 CreateBuilder<
669 E,
670 ContractRef,
671 Set<LimitParamsV2>,
672 Set<ExecutionInput<Args, ScaleEncoding>>,
673 Set<ReturnType<RetType>>,
674 >
675where
676 E: Environment,
677 ContractRef: FromAddr + crate::ContractReverseReference,
678 <ContractRef as crate::ContractReverseReference>::Type:
679 crate::reflect::ContractConstructorDecoder,
680 <ContractRef as crate::ContractReverseReference>::Type:
681 crate::reflect::ContractMessageDecoder,
682 Args: AbiEncodeWith<ScaleEncoding>,
683 RetType: ConstructorReturnType<ContractRef>,
684{
685 /// todo check comment
686 /// Instantiates the contract and returns its account ID back to the caller.
687 ///
688 /// # Panics
689 ///
690 /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an
691 /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle
692 /// those use the [`try_instantiate`][`CreateBuilder::try_instantiate`] method
693 /// instead.
694 #[inline]
695 #[cfg(feature = "unstable-hostfn")]
696 pub fn instantiate(self) -> <RetType as ConstructorReturnType<ContractRef>>::Output {
697 self.params().instantiate()
698 }
699
700 /// todo check comment
701 /// Instantiates the contract and returns its account ID back to the caller.
702 ///
703 /// # Note
704 ///
705 /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner
706 /// [`ink::primitives::LangError`][`ink_primitives::LangError`], both of which can be
707 /// handled by the caller.
708 #[inline]
709 #[cfg(feature = "unstable-hostfn")]
710 pub fn try_instantiate(
711 self,
712 ) -> Result<
713 ink_primitives::ConstructorResult<
714 <RetType as ConstructorReturnType<ContractRef>>::Output,
715 >,
716 Error,
717 > {
718 self.params().try_instantiate()
719 }
720}