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}