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