7

The question is about both types of pallets-coupling. let's say we have 3 pallets A,B, and C.

C is tightly coupled to B (C Config is added in B Config), and we want to do a loose Coupling of A and B in order to use functions and types available in B and C (in pallet A, I do a pub use pallet_B::pallet as B; and in order to use types defined in B, inside A/pub mod pallet{..}, I do: use pallet_B::{Type0, Type1, Type2};).

Now, this all work fine except in one particular case: for Types defined in B as Type Type_x<T>=<T as C::Config>::Type_y;. If I try to import Type_x in A from B, I get an Error saying that some traits from C::Config are missing, and this is what I don't understand. My perception is that if B is already accessing everything in C through tight-coupling, then there is no need to call C from A's pallet or A's runtime....What am I misunderstanding?

For a concrete example of what I am trying to do, you can take a look at https://github.com/Fair-Squares/fair-squares/tree/structure_template/pallets

A --> Template, B-->nft, C-->orml-nft

Make sure you're looking at the "structure_template" branch.

EDIT:

lib.rs

pub mod pallet1;
pub mod pallet2;
pub mod bad_pallet3;

pallet1.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::pallet_prelude::*;

    #[pallet::config]
    pub trait Config: frame_system::Config {
        type ClassId: Parameter + Member + Default + Copy;
    }

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::storage]
    pub type MyValue<T: Config> = StorageValue<_, u32, ValueQuery>;
}

impl<T: Config> Pallet<T> {
    pub fn get_value() -> u32 {
        <MyValue<T>>::get()
    }
}

pallet2.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use crate::pallet1;
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config + pallet1::Config {}

    pub type ClassIdOf<T> = <T as pallet1::Config>::ClassId;

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(0)]
        pub fn get_value_directly(_origin: OriginFor<T>) -> DispatchResult {
            let _value = pallet1::MyValue::<T>::get();
            Ok(())
        }
        #[pallet::weight(0)]
        pub fn set_value_directly(_origin: OriginFor<T>, value: u32) -> DispatchResult {
            pallet1::MyValue::<T>::put(value);
            Ok(())
        }
        #[pallet::weight(0)]
        pub fn get_value_public_function(_origin: OriginFor<T>) -> DispatchResult {
            let _value = pallet1::Pallet::<T>::get_value();

            Ok(())
        }
    }
}

bad_pallet3.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use crate::{pallet2};
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    use pallet2::pallet::ClassIdOf;

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config{}

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(0)]
        pub fn get_value_directly(_origin: OriginFor<T>, _cl: ClassIdOf<T>) -> DispatchResult {
            let _value = 5;
            Ok(())
        }

        #[pallet::weight(0)]
        pub fn set_value_directly(_origin: OriginFor<T>, value: u32) -> DispatchResult {
            let _val=value;
            Ok(())
        }

        
    }
}

ClassIdOf in Bad_pallet3 is my problem…I only need ClassIdOf in pallet3, so I leave out pallet2::Config, which is probably the source of the error—-> is there a way to do this without adding pallet2::Config to pallet3::Config (which is a tight-coupling if I understand things correctly…)

6
  • Can you post a minimal example of code that is failing, and the exact error that you are seeing?
    – Shawn Tabrizi
    Mar 20, 2022 at 15:55
  • I did an edit of the initial post. Hope this helps to clarify the nature of the problem... Mar 21, 2022 at 12:11
  • 1
    @ShawnTabrizi, added a minimal example, inspired by one of your previous answer…. Mar 23, 2022 at 0:56
  • I implemented your code, and made it "compile", but i dont understand what your problem is. Can you please update the code above to make the error appear again, but make sure that you are doing other things which are REAL rust?
    – Shawn Tabrizi
    Mar 23, 2022 at 1:45
  • I have a few questions concerning your implementation: you are doing ‘use crate::{pallet1, pallet2};’ in pallet3, but pallet2 is already using pallet1 right? (pub use crate::pallet1; in pallet2 should work??) Mar 23, 2022 at 1:59

1 Answer 1

4

The problem is that you are never doing any "loose coupling".

Loose coupling is when you bridge two pallets through a shared trait interface.

You need to define a trait like:

pub trait LooseInterface {
    type ClassId: Parameter + Member + Default + Copy;
}

And use it across the pallets you want to bridge.

Here is an example working solution where pallet 3 is loosely coupled to pallet 2:

lib.rs

pub mod pallet1;
pub mod pallet2;
pub mod pallet3;

use frame_support::pallet_prelude::*;

// This trait is in a shared location. Probably it should live in some primitive library.
pub trait LooseInterface {
    type ClassId: Parameter + Member + Default + Copy;
}

pallet1.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::pallet_prelude::*;

    #[pallet::config]
    pub trait Config: frame_system::Config {
        type ClassId: Parameter + Member + Default + Copy;
    }

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::storage]
    pub type MyValue<T: Config> = StorageValue<_, u32, ValueQuery>;
}

impl<T: Config> Pallet<T> {
    pub fn get_value() -> u32 {
        <MyValue<T>>::get()
    }
}

pallet2.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use crate::pallet1;
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config + pallet1::Config {}

    pub type ClassIdOf<T> = <T as pallet1::Config>::ClassId;

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(0)]
        pub fn get_value_directly(_origin: OriginFor<T>) -> DispatchResult {
            let _value = pallet1::MyValue::<T>::get();
            Ok(())
        }
        #[pallet::weight(0)]
        pub fn set_value_directly(_origin: OriginFor<T>, value: u32) -> DispatchResult {
            pallet1::MyValue::<T>::put(value);
            Ok(())
        }
        #[pallet::weight(0)]
        pub fn get_value_public_function(_origin: OriginFor<T>) -> DispatchResult {
            let _value = pallet1::Pallet::<T>::get_value();

            Ok(())
        }
    }

    // Here we implement the shared trait for this pallet.
    // Now it can be loosely coupled to other pallets which expect it.
    impl<T: Config> crate::LooseInterface for Pallet<T> {
        type ClassId = ClassIdOf<T>;
    }
}

pallet3.rs

#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[frame_support::pallet]
pub mod pallet {
    use frame_support::pallet_prelude::*;
    use frame_system::pallet_prelude::*;

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config {
        // Here we expect "something" to provide the `LooseInterface`.
        // When building your runtime, you would link pallet 3 to pallet 2
        // which will loosely couple them, since you could also use another
        // pallet which implements this trait instead.
        type Interface: crate::LooseInterface;
    }

    type ClassIdOf<T> = <<T as Config>::Interface as crate::LooseInterface>::ClassId;

    #[pallet::call]
    impl<T: Config> Pallet<T> {
        #[pallet::weight(0)]
        pub fn get_value_directly(_origin: OriginFor<T>, _cl: ClassIdOf<T>) -> DispatchResult {
            let _value = 5;
            Ok(())
        }

        #[pallet::weight(0)]
        pub fn set_value_directly(_origin: OriginFor<T>, value: u32) -> DispatchResult {
            let val = value;
            Ok(())
        }
    }
}

Finally the loose coupling would happen in the runtime configuration:

/// Configure the pallet-template in pallets/template.
impl pallet3::Config for Runtime {
    type Interface = pallet2::Pallet<Runtime>;
}
1
  • I have been wondering what the Member trait mean for a long time...
    – h4x3rotab
    Mar 23, 2022 at 6:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.