use crate::reflect::{
ContractEnv,
DispatchError,
};
use core::{
convert::Infallible,
mem::ManuallyDrop,
};
use ink_env::{
Environment,
ReturnFlags,
};
use ink_primitives::{
Key,
KeyPtr,
};
use ink_storage::traits::{
push_spread_root,
SpreadAllocate,
SpreadLayout,
};
pub trait ContractRootKey {
const ROOT_KEY: Key;
}
#[inline]
pub fn deny_payment<E>() -> Result<(), DispatchError>
where
E: Environment,
{
let transferred = ink_env::transferred_value::<E>();
if transferred != <E as Environment>::Balance::from(0_u32) {
return Err(DispatchError::PaidUnpayableMessage)
}
Ok(())
}
#[derive(Debug, Copy, Clone)]
pub struct ExecuteConstructorConfig {
pub payable: bool,
}
#[inline]
pub fn execute_constructor<Contract, F, R>(
config: ExecuteConstructorConfig,
f: F,
) -> Result<(), DispatchError>
where
Contract: SpreadLayout + ContractRootKey + ContractEnv,
F: FnOnce() -> R,
<private::Seal<R> as ConstructorReturnType<Contract>>::ReturnValue: scale::Encode,
private::Seal<R>: ConstructorReturnType<Contract>,
{
if !config.payable {
deny_payment::<<Contract as ContractEnv>::Env>()?;
}
let result = ManuallyDrop::new(private::Seal(f()));
match result.as_result() {
Ok(contract) => {
let root_key = <Contract as ContractRootKey>::ROOT_KEY;
push_spread_root::<Contract>(contract, &root_key);
Ok(())
}
Err(_) => {
ink_env::return_value::<
<private::Seal<R> as ConstructorReturnType<Contract>>::ReturnValue,
>(
ReturnFlags::default().set_reverted(true),
result.return_value(),
)
}
}
}
#[inline]
pub fn initialize_contract<Contract, F, R>(
initializer: F,
) -> <R as InitializerReturnType<Contract>>::Wrapped
where
Contract: ContractRootKey + SpreadAllocate,
F: FnOnce(&mut Contract) -> R,
R: InitializerReturnType<Contract>,
{
let mut key_ptr = KeyPtr::from(<Contract as ContractRootKey>::ROOT_KEY);
let mut instance = <Contract as SpreadAllocate>::allocate_spread(&mut key_ptr);
let result = initializer(&mut instance);
result.into_wrapped(instance)
}
mod private {
pub trait Sealed {}
impl Sealed for () {}
impl<T, E> Sealed for Result<T, E> {}
#[repr(transparent)]
pub struct Seal<T>(pub T);
impl<T> Sealed for Seal<T> {}
}
pub trait ConstructorReturnType<C>: private::Sealed {
const IS_RESULT: bool = false;
type Error;
type ReturnValue;
fn as_result(&self) -> Result<&C, &Self::Error>;
fn return_value(&self) -> &Self::ReturnValue;
}
impl<C> ConstructorReturnType<C> for private::Seal<C> {
type Error = Infallible;
type ReturnValue = ();
#[inline]
fn as_result(&self) -> Result<&C, &Self::Error> {
Ok(&self.0)
}
#[inline]
fn return_value(&self) -> &Self::ReturnValue {
&()
}
}
impl<C, E> ConstructorReturnType<C> for private::Seal<Result<C, E>> {
const IS_RESULT: bool = true;
type Error = E;
type ReturnValue = Result<C, E>;
#[inline]
fn as_result(&self) -> Result<&C, &Self::Error> {
self.0.as_ref()
}
#[inline]
fn return_value(&self) -> &Self::ReturnValue {
&self.0
}
}
pub trait InitializerReturnType<C>: private::Sealed {
type Wrapped;
fn into_wrapped(self, wrapped: C) -> Self::Wrapped;
}
impl<C> InitializerReturnType<C> for () {
type Wrapped = C;
#[inline]
fn into_wrapped(self, wrapped: C) -> C {
wrapped
}
}
impl<C, E> InitializerReturnType<C> for Result<(), E> {
type Wrapped = Result<C, E>;
#[inline]
fn into_wrapped(self, wrapped: C) -> Self::Wrapped {
self.map(|_| wrapped)
}
}