-
Notifications
You must be signed in to change notification settings - Fork 468
Description
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.