Skip to content

Commit

Permalink
Added ink::combine_extensions! helper macro to simplify the definit…
Browse files Browse the repository at this point in the history
…ion of the combined chain extension
  • Loading branch information
xgreenx committed Nov 8, 2023
1 parent ce21a89 commit eb78377
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 32 deletions.
75 changes: 75 additions & 0 deletions crates/ink/src/chain_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,78 @@ mod private {
/// Seals the `Output` trait so that it cannot be implemented outside this module.
pub trait OutputSealed {}
}

/// Macro defines the combined chain extension via structure definition.
/// Each sub-extension can be accessed by the corresponding field.
///
/// The macro expects a structure definition as an input where each field should
/// implement [`ChainExtensionInstance`]. Usually, this trait is implemented
/// by the [`crate::chain_extension`] macro during the definition of the chain extension.
///
/// ```rust
/// #[ink::scale_derive(TypeInfo)]
/// struct ExtensionOne;
/// impl ink::ChainExtensionInstance for ExtensionOne {
/// type Instance = Self;
///
/// fn instantiate() -> Self::Instance {
/// Self {}
/// }
/// }
///
/// #[ink::scale_derive(TypeInfo)]
/// struct ExtensionTwo;
/// impl ink::ChainExtensionInstance for ExtensionTwo {
/// type Instance = Self;
///
/// fn instantiate() -> Self::Instance {
/// Self {}
/// }
/// }
///
/// ink::combine_extensions! {
/// /// Defines a combined extension with [`ExtensionOne`] and [`ExtensionTwo`].
/// struct Combined {
/// /// This field can be used to access the first extension like
/// /// `self.env().extension().extension_one` in the contract's context.
/// extension_one: ExtensionOne,
/// /// This field can be used to access the second extension like
/// /// `self.env().extension().extension_two` in the contract's context.
/// extension_two: ExtensionTwo,
/// }
/// }
/// ```
#[macro_export]
macro_rules! combine_extensions {
($(#[$meta:meta])* $vis:vis struct $name:ident {
$($(#[$field_meta:meta])* $field_vis:vis $field_name:ident: $field_type:ty,)*
}) => {
$(#[$meta])*
#[::ink::scale_derive(TypeInfo)]
$vis struct $name {
$($(#[$field_meta])* $field_vis $field_name: $field_type,)*
}

const _: () = {
/// Each chain extension has an abstract type $name that describes
/// it and the actual instance that provides access to methods.
/// This structure is an instance that is returned by the `self.env().extension()` call.
///
/// Because it is a combination of corresponding sub-instances, we need to initialize
/// each sub-instance in the same way by calling [`ChainExtensionInstance::instantiate`].
pub struct Private {
$($(#[$field_meta])* $field_vis $field_name: <$field_type as ::ink::ChainExtensionInstance>::Instance,)*
}

impl ::ink::ChainExtensionInstance for $name {
type Instance = Private;

fn instantiate() -> Self::Instance {
Self::Instance {
$($field_name: <$field_type as ::ink::ChainExtensionInstance>::instantiate(),)*
}
}
}
};
}
}
46 changes: 14 additions & 32 deletions integration-tests/combined-extension/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,27 @@ use ink::{
DefaultEnvironment,
Environment,
},
ChainExtensionInstance,
};
use psp22_extension::Psp22Extension;
use rand_extension::{
FetchRandom,
RandomReadErr,
};

/// This extension combines the [`FetchRandom`] and [`Psp22Extension`] extensions.
/// It implements the [`ChainExtensionInstance`] trait by simply calling
/// the corresponding sub-extension implementation.
/// It is possible to combine any number of extensions in this way.
#[ink::scale_derive(TypeInfo)]
pub struct CombinedChainExtension;

/// Each chain extension has an abstract type([`CombinedChainExtension`]) that describes
/// it and the actual instance that provides access to methods.
/// This structure is an instance that is returned by the `self.env().extension()` call.
///
/// Because it is a combination of corresponding sub-instances, we need to initialize
/// each sub-instance in the same way by calling [`ChainExtensionInstance::instantiate`].
pub struct CombinedInstance {
/// The instance of the [`Psp22Extension`] chain extension.
///
/// It provides you access to `PSP22` functionality.
pub psp22: <Psp22Extension as ChainExtensionInstance>::Instance,
/// The instance of the [`FetchRandom`] chain extension.
ink::combine_extensions! {
/// This extension combines the [`FetchRandom`] and [`Psp22Extension`] extensions.
/// It is possible to combine any number of extensions in this way.
///
/// It provides you access to randomness functionality.
pub rand: <FetchRandom as ChainExtensionInstance>::Instance,
}

impl ChainExtensionInstance for CombinedChainExtension {
type Instance = CombinedInstance;

fn instantiate() -> Self::Instance {
CombinedInstance {
psp22: <Psp22Extension as ChainExtensionInstance>::instantiate(),
rand: <FetchRandom as ChainExtensionInstance>::instantiate(),
}
/// This structure is an instance that is returned by the `self.env().extension()` call.
pub struct CombinedChainExtension {
/// The instance of the [`Psp22Extension`] chain extension.
///
/// It provides you access to `PSP22` functionality.
pub psp22: Psp22Extension,
/// The instance of the [`FetchRandom`] chain extension.
///
/// It provides you access to randomness functionality.
pub rand: FetchRandom,
}
}

Expand Down Expand Up @@ -81,6 +62,7 @@ mod combined_extension {
/// The example shows how to call each extension and test it,
/// so we don't need any state to save.
#[ink(storage)]
#[derive(Default)]
pub struct CombinedExtensionContract;

impl CombinedExtensionContract {
Expand Down

0 comments on commit eb78377

Please sign in to comment.