implementation

Attribute Macro implementation 

Source
#[implementation]
Expand description

Chain extension implementation for use with Substrate-based nodes.

§Description

This macro generates the necessary trait implementations for you to use your chain extension with Substrate runtime.

This macro checks for the generics that you use in your impl block.

use obce::substrate::{
    frame_system::Config as SysConfig,
    pallet_contracts::Config as ContractConfig,
    sp_runtime::traits::StaticLookup,
    ChainExtensionEnvironment,
    ExtensionContext
};

pub struct ChainExtension;

#[obce::definition]
pub trait ChainExtensionDefinition {
    fn extension_method(&self);
}

#[obce::implementation]
impl<'a, E, T, Env> ChainExtensionDefinition for ExtensionContext<'a, E, T, Env, ChainExtension>
where
    T: SysConfig + ContractConfig,
    <<T as SysConfig>::Lookup as StaticLookup>::Source: From<<T as SysConfig>::AccountId>,
    Env: ChainExtensionEnvironment<E, T>,
{
    fn extension_method(&self) {
        // Do awesome stuff!
    }
}

§Generics

E represents the external environment in which smart contracts are being executed. When building chain extension without OBCE, it is usually bounded by pallet_contracts::chain_extension::Ext, providing you access to methods that interacts with the execution environment. However, to provide you with better testing capabilities OBCE does not bound the E generic itself, resorting to bound the Env with it instead.

T represents your configuration type, which can be bounded by pallet-specific configuration traits (such as pallet_contracts::pallet::Config and frame_system::Config).

Env generic is used to represent the OBCE-specific chain extension environment, which is more easily testable, and can additionally be bounded by any trait you want to use. For example, you can add a trait that represents your chain-specific pallet and use it inside of your chain extension.

§Weight charging

You can use #[obce(weight(dispatch = ...))] to automatically charge weight based on a pallet call dispatch information.

dispatch accepts a full path to pallet’s call (for example, pallet_example::Pallet::<T>::my_call).

OBCE will attempt to automatically obtain dispatch info based on the arguments passed to your chain extension method.

If pallet’s call arguments and your chain extension method arguments are different, you can use args to override them: #[obce(weight(dispatch = "pallet_example::Pallet::<T>::my_call", args = "some_val,123"))].

You can also use #[obce(weight(expr = ...))] to charge weight without pallet calls. In this case, you can simply provide any expression which returns Weight: #[obce(weight(expr = "Weight::from_parts(ref_time, proof_size)"))].

OBCE also provides you with a pre-charging feature, which charges weight before any data parsing is done, making sure that weight is paid even if the call is not successful:

use obce::substrate::{
    sp_weights::Weight,
    frame_system::Config as SysConfig,
    pallet_contracts::Config as ContractConfig,
    sp_runtime::traits::StaticLookup,
    ChainExtensionEnvironment,
    ExtensionContext
};

pub struct ChainExtension;

#[obce::definition]
pub trait ChainExtensionDefinition {
    fn extension_method(&mut self, val: u64);
}

#[obce::implementation]
impl<'a, E, T, Env> ChainExtensionDefinition for ExtensionContext<'a, E, T, Env, ChainExtension>
where
    T: SysConfig + ContractConfig,
    <<T as SysConfig>::Lookup as StaticLookup>::Source: From<<T as SysConfig>::AccountId>,
    Env: ChainExtensionEnvironment<E, T>,
{
    #[obce(weight(expr = "Weight::from_parts(123, 0)", pre_charge))]
    fn extension_method(&mut self, _val: u64) {
        self.pre_charged().unwrap();
    }
}

fn main() {}

§Usage example

use obce::substrate::{
    frame_system::{Config as SysConfig, RawOrigin},
    pallet_contracts::Config as ContractConfig,
    sp_runtime::traits::StaticLookup,
    ChainExtensionEnvironment,
    ExtensionContext
};

pub struct ChainExtension;

#[obce::definition]
pub trait ChainExtensionDefinition {
    fn extension_method(&mut self, val: u64);
}

#[obce::implementation]
impl<'a, E, T, Env> ChainExtensionDefinition for ExtensionContext<'a, E, T, Env, ChainExtension>
where
    T: SysConfig + ContractConfig + pallet_example::Config,
    <<T as SysConfig>::Lookup as StaticLookup>::Source: From<<T as SysConfig>::AccountId>,
    Env: ChainExtensionEnvironment<E, T>,
{
    #[obce(weight(dispatch = "pallet_example::Pallet::<T>::test_method", args = "123"))]
    fn extension_method(&mut self, val: u64) {
        // ...
    }
}

§Ext trait bounds

You may notice that the example above doesn’t have E: Ext<T = T> bound, which is required when calling your chain extension via pallet_contracts::chain_extension::ChainExtension.

This is because OBCE automatically generates two separate trait implementations for your chain extension struct - obce::substrate::CallableChainExtension and pallet_contracts::chain_extension::ChainExtension.

Only when generating the latter OBCE automatically adds E: Ext<T = T> bound, while still providing you capabilities to manually add E: Ext<T = T> on the implementation trait bounds to allow Ext trait usage inside implementation methods:

use obce::substrate::{
    frame_system::{Config as SysConfig, RawOrigin},
    pallet_contracts::{
        chain_extension::Ext,
        Config as ContractConfig,
    },
    sp_runtime::traits::StaticLookup,
    ChainExtensionEnvironment,
    ExtensionContext
};

pub struct ChainExtension;

#[obce::definition]
pub trait ChainExtensionDefinition {
    fn extension_method(&mut self, val: u64);
}

#[obce::implementation]
impl<'a, E, T, Env> ChainExtensionDefinition for ExtensionContext<'a, E, T, Env, ChainExtension>
where
    T: SysConfig + ContractConfig + pallet_example::Config,
    <<T as SysConfig>::Lookup as StaticLookup>::Source: From<<T as SysConfig>::AccountId>,
    Env: ChainExtensionEnvironment<E, T>,
    E: Ext<T = T>,
{
    fn extension_method(&mut self, val: u64) {
        // Ext trait can be used here
    }
}

This is done to ease chain extension environment generalization during testing.