Skip to content

Proposal to initialize the contract on demand #1388

@xgreenx

Description

@xgreenx

With the introduction of delegate_call, we can have situations when one contract X uses another contract Y as a blueprint. The blueprint may have totally another storage layout. Then the X do the first delegate_call to Y, Y can't load the contract's storage from the X's storage because it is not initialized(Y's constructor was not called).

It creates a situation when we need support initializing the storage during the execution of the #[ink(message)]. The user can always wrap his storage into another structure and implement the Storable trait for this struct that supports initialization on demand. But we can add native support of it into the ink!.

The proposal is to introduce the OnCallInitialize trait.

/// The contract can implement that trait to support initialization on the runtime
 /// if it is unable to pull from the storage.
 ///
 /// It can be in several cases:
 /// - The contract doesn't have a constructor. That initializer can be an alternative for the constructor.
 /// - The constructor was not called due to upgradeability, `Proxy` or `Diamond` pattern.
 /// - The storage was moved or corrupted.
 ///
 /// If the trait is not implemented, the storage behavior is the default.
 /// It should be first initialized by the constructor.
 pub trait OnCallInitializer: Default {
     /// `Default::default` creates the instance of the contract.
     /// After the `initialize` method is called on that instance.
     /// The developer can do everything that he wants during initialization or do nothing.
     fn initialize(&mut self);
 }

If the contract implements Default and OnCallInitialize traits, the ink! will try to load the contract from the storage, and in the case of fail, it will initialize it.

 impl<T: OnCallInitializer + Storable> PullOrInit<T> {
     #[allow(dead_code)]
     pub fn pull_or_init(key: &Key) -> T {
         let maybe_instance = ink_env::get_contract_storage::<Key, T>(key);
         match maybe_instance {
             Ok(None) | Err(_) => {
                 let mut instance = Default::default();
                 <T as OnCallInitializer>::initialize(&mut instance);
                 instance
             }
             Ok(Some(storage)) => storage,
         }
     }
 }

This idea was implemented in #1331. But we decided to discuss it separately because it is an important API change. After implementation "initialization on demand" we need to return back delegate-call example, removed in the mentioned PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-discussionAn issue for discussion for a given topic.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions