Substrate pallet loosely coupling example between 2 custom pallets - substrate

This official docs is about custom pallet using an substrate's pallets.
https://docs.substrate.io/how-to-guides/v3/pallet-design/loose-coupling/#2-import-the-trait
I don't know exactly how to do this with 2 custom pallets?

Here how I dit it:
For example: pallet BoTrading need to call get_suitable_lp on pallet BoLiquidity
pallets/BoLiquidity/src/lib.rs
/// Internal helpers fn
impl<T: Config> Pallet<T> {
fn pick_a_suitable_lp() -> Option<u32> {
Some(99999)
}
}
pub trait BoLiquidityInterface{
fn get_suitable_lp()->Option<u32>;
}
impl<T: Config> BoLiquidityInterface for Pallet<T> {
fn get_suitable_lp()->Option<u32>{
Self::pick_a_suitable_lp()
}
}
pallets/BoTrading/src/lib.rs
pub mod pallet {
...
use pallet_bo_liquidity::BoLiquidityInterface;
#[pallet::config]
pub trait Config: frame_system::Config {
...
type BoLiquidity: BoLiquidityInterface;
}
...
// call other pallet some where inside this pallet:
let lp = T::BoLiquidity::get_suitable_lp();
...
}
pallets/BoTrading/Cargo.toml
[dependencies.pallet-bo-liquidity]
default-features = false
path = '../BoLiquidity'
version = '0.0.1-dev'
Remember to include pallets in cargo toml of the node runtime, this code bellow focus on important thing only:
runtime/src/lib.rs
impl pallet_bo_trading::Config for Runtime {
...
type BoLiquidity = BoLiquidityModule;
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
...
BoLiquidityModule: pallet_bo_liquidity,
}
);

Related

Compile error in Substrate smart contract 'the trait bound ink_storage::Vec<...>: WrapperTypeEncode is not satisfied'

I'm a newbie on Substrate and blockchain development. Currently, I'm writing a smart contract using Substrate with ink!. I've defined a custom type (struct Person in the code) and created the vector of this type in the contract's storage.
I then create contract's functions for working with that vector data. But the code fails to compile. There are errors with get_person_list() function. The first one is 'the trait bound ink_storage::Vec<Person>: WrapperTypeEncode is not satisfied'.
However, if I remove this function, the code can be compiled successfully. How can I solve this compile error? thanks.
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang as ink;
#[ink::contract]
mod test {
use ink_prelude::string::String;
use ink_storage::collections::Vec;
use ink_storage::traits::{
SpreadLayout,
PackedLayout,
};
use scale::{Encode, Decode};
#[derive(Debug, Clone, Encode, Decode, SpreadLayout, PackedLayout)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub struct Person {
name: String,
age: u32,
}
#[ink(storage)]
pub struct Test {
person_list: Vec<Person>,
}
impl Test {
#[ink(constructor)]
pub fn new() -> Self {
Self { person_list: Vec::new() }
}
#[ink(message)]
pub fn add_person(&mut self, name: String, age: u32) {
self.person_list.push(Person { name, age });
}
#[ink(message)]
pub fn get_person(&self, index: u32) -> Option<Person> {
self.person_list.get(index).cloned()
}
#[ink(message)]
pub fn person_list_count(&self) -> u32 {
self.person_list.len() as u32
}
#[ink(message)]
pub fn get_person_list(&self) -> Vec<Person> {
self.person_list.clone()
}
}
}
I think it will work if you use use ink_prelude::vec::Vec; instead of use ink_storage::collections::Vec;.

How do I access storage and functions in a pallet from another pallet?

I have one common pallet which is to be used by other pallets. For example, below is the common shared pallet:
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
mod tests;
pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
}
type AccountIdOf<T> = <T as system::Trait>::AccountId;
type BalanceOf<T> = <<T as Trait>::Currency as Currency<AccountIdOf<T>>>::Balance;
type StructInfoOf<T> = StructInfo<AccountIdOf<T>, <T as system::Trait>::BlockNumber>;
#[derive(Encode, Decode, Default, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct StructInfo<AccountId, BlockNumber> {
name: Vec<u8>,
owner: AccountId,
created: BlockNumber,
}
decl_storage! {
trait Store for Module<T: Trait> as TokenStore {
pub Data get(fn data): map hasher(blake2_128_concat) u32 => Option<StructInfoOf<T>>;
pub DataCount get(fn data_count): u32;
}
}
decl_event!(
pub enum Event<T>
where
AccountId = <T as system::Trait>::AccountId,
Balance = BalanceOf<T>,
{
/// Test event
TestEvent(u32, Balance, AccountId),
}
);
decl_error! {
pub enum Error for Module<T: Trait> {
TestError
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default;
#[weight = 10_000]
pub fn test_dispatch_call(origin) -> DispatchResult {
let caller = ensure_signed(origin)?;
Ok(())
}
}
}
impl<T: Trait> Module<T> {
pub fn public_function(value: u32) -> BalanceOf<T> {
let value: u32 = 0;
Self::data_count()
}
}
but I am not sure how I can call Data, DataCount, test_dispatch_call, public_function from the common module.
Add your pallet that need in Cargo.toml in [dependencies] like pallet-test = {default-features = false, path ='../test'} and in [features] [std] like 'pallet-test/std'
In current pallet: add pallet-test in pub trait Trait (v2) or pub trait Config(v3) like pub trait Config: frame_system::Config + pallet_test::Config{}
Access Function: <pallet_test::Module<T>>::function_test();

String attribute set in init method always returns empty string

I have the following struct with impl:
#[near_bindgen]
#[derive(Default, Serialize, Deserialize, BorshDeserialize, BorshSerialize, Debug)]
pub struct MyStruct {
owner: String
}
#[near_bindgen(init => new)]
impl MyStruct {
fn new() -> Self {
Self {
owner: "bob".to_string()
}
}
fn get_owner(&self) -> String {
return self.owner;
}
}
Then I deploy the contract using near deploy my_contract --masterAccount myAccount
If I call get_owner using near-shell: near call my_contract get_owner --accountId=myAccount It always returns "" instead of the expected "bob".
It seems like the new method might not get called on deployment.
Initializer doesn't automatically get called on deploy. deploy just deploys the code and doesn't call anything on the contract. We should probably add a new method to shell, that does deploy_and_call. But for now just call new manually.
The reason why we don't initialize automatically is that initializer might take additional arguments. You can pass an owner to new method. Here is an example how to use initializer with custom arguments and as well as how to make sure a contract can't be called without initialization:
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct FunToken {
/// AccountID -> Account details.
pub accounts: Map<AccountId, Account>,
/// Total supply of the all token.
pub total_supply: Balance,
}
impl Default for FunToken {
fn default() -> Self {
env::panic(b"Not initialized");
unreachable!();
}
}
#[near_bindgen(init => new)]
impl FunToken {
pub fn new(owner_id: AccountId, total_supply: Balance) -> Self {
let mut ft = Self { accounts: Map::new(b"a".to_vec()), total_supply };
let mut account = ft.get_account(&owner_id);
account.balance = total_supply;
ft.accounts.insert(&owner_id, &account);
ft
}
}
From here: https://github.com/nearprotocol/near-bindgen/blob/master/examples/fun-token/src/lib.rs#L52-L77
Basically it panics during Default call, so non initialized contract can't be called.
Initializer functions are usually used when you need to parametrize the initialization of the contract. If there are no parameters then just implement Default trait:
impl Default for MyStruct {
fn default() -> Self {
Self {
owner: "bob".to_string()
}
}}

In Substrate is there a way to use storage and functions from one custom module in another?

I have seen the Substrate Tutorial on creating crates of individual Substrate Runtime modules here in order to re-use the functionality, but I wondered if there is a way for one custom module to access the storage or functions from another custom module?
Something along these lines:
/// In ModuleA
pub type IndexType = u64;
decl_storage! {
trait Store for Module<T: Trait> as ModuleA {
pub MyIndexCount get(my_index_count): Option<IndexType>;
}
}
And then inside ModuleB - what do I need to do to use/include the functionality of ModuleA, and how do I call it?
/// In ModuleB
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
pub fn edit_index(origin) -> Result {
let sender = ensure_signed(origin)?;
// --->>>> I want to read some storage from ModuleA whilst inside ModuleB
let c: IndexType = ReadStorageFromModuleA >>> my_index_count().ok_or("Storage Read Error: cannot get index")?;
// change storage in ModuleA from ModuleB
WriteToStorageInModuleA <MyIndexCount<T>>::put(&c + 1);
Ok(())
}
}
}
If you are building a module (module2) which has a direct dependency on another module (module1), you must inherit module1's trait in module2's trait definition:
pub trait Trait: module1::Trait {
...
}
To access public storage items from module1 in module2, you need to do the following:
Import the appropriate storage trait to access the storage API: StorageValue, StorageMap, etc...
Access the public storage through module1's storage type
<module1::Something<T>>::get()
<module1::Something<T>>::put()
etc...
To access other public functions from module 1 in module 2, you need to use the Module type:
<module1::Module<T>>::public_function();
Here is a simple example of two modules interacting in this way:
module1.rs
Note that all the things in this module are marked public (pub)
use support::{decl_module, decl_storage, StorageValue};
pub trait Trait: system::Trait {}
decl_storage! {
trait Store for Module<T: Trait> as TemplateModule {
pub Something: u32;
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
}
}
impl<T: Trait> Module<T> {
pub fn get_value() -> u32 {
<Something<T>>::get()
}
}
module2.rs
use support::{decl_module, decl_event, StorageValue, dispatch::Result};
use system::ensure_signed;
use crate::module1;
pub trait Trait: module1::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
decl_module! {
/// The module declaration.
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
pub fn get_value_directly(origin) -> Result {
let who = ensure_signed(origin)?;
let value = <module1::Something<T>>::get();
Self::deposit_event(RawEvent::ValueIs(value, who));
Ok(())
}
pub fn set_value_directly(origin, value: u32) -> Result {
let _ = ensure_signed(origin)?;
<module1::Something<T>>::put(value);
Ok(())
}
pub fn get_value_public_function(origin) -> Result {
let who = ensure_signed(origin)?;
let value = <module1::Module<T>>::get_value();
Self::deposit_event(RawEvent::ValueIs(value, who));
Ok(())
}
}
}
decl_event!(
pub enum Event<T> where <T as system::Trait>::AccountId {
ValueIs(u32, AccountId),
}
);

Replace a struct member with a new value that uses the previous value

I have a struct which owns a boxed value of some trait type. The struct itself also implements the same trait. I would like to replace the value with a new instance of the same struct, which wraps it.
The following code, which does not compile, should make it more clear what I am trying to do:
trait T {}
struct S {
t: Box<dyn T>,
}
impl T for S {}
impl S {
fn new(t: Box<dyn T>) -> Self {
Self { t }
}
fn wrap_t(&mut self) {
self.t = Box::new(Self::new(self.t))
}
}
This fails:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:14:37
|
14 | self.t = Box::new(Self::new(self.t))
| ^^^^ cannot move out of borrowed content
Implementing wrap_t like this does compile:
use std::mem;
fn wrap_t(&mut self) {
unsafe {
let old_t = mem::replace(&mut self.t, mem::uninitialized());
let new_t = Box::new(Self::new(old_t));
let uninit = mem::replace(&mut self.t, new_t);
mem::forget(uninit);
}
}
I wonder if there is a safe way to do this.
The only unsafe function you are using is mem::uninitialized. You need something to pass to mem::replace, but implementing Default won't work because default() returns Self, which prevents it from being object-safe. Similarly, you can't implement Clone to duplicate the old value, since clone() also returns Self.
You can just implement a dummy type for the purpose though:
struct Dummy;
impl T for Dummy {}
fn wrap_t(&mut self) {
let old_t = mem::replace(&mut self.t, Box::new(Dummy));
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
You also won't need the mem::forget here now either (I'm assuming that was there to prevent undefined behaviour when the uninitialised memory was dropped).
As an alternative to Clone, you can roll your own own, which clones to a Box<dyn T>, avoiding having a Self in the method signature, so the trait stays object safe:
trait T: Debug {
fn clone_in_box(&self) -> Box<dyn T>;
}
impl T for S {
fn clone_in_box(&self) -> Box<dyn T> {
Box::new(S {
t: self.t.clone_in_box(),
})
}
}
fn wrap_t(&mut self) {
let cloned = self.clone_in_box();
let old_t = mem::replace(&mut self.t, cloned);
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
There is also an alternative design, which is much simpler to understand when reading the code. That is just to consume self and return a new object:
fn wrap_t(self) -> Self {
Self::new(Box::new(Self::new(self.t)))
}
And instead of this:
s.wrap_t();
You would do:
s = s.wrap_t();

Resources