# Pallet Decoupling ## Why decoupling - Verbosity - Readability - Debug - Maintainability - ... ## Coupled Way Example: ```rust= // Definition pub trait Trait: frame_sysmte::Trait + module1::Trait + module2::Trait + ... { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; ... } // usage { ... // read storages let module1_value = <module1::ModuleStorage1<T>>::get(key1); let module2_value = <module2::ModuleStorage2<T>>::get(key2); // write storages <module1::ModuleStorage1<T>>::mutate(&key1, |val| { // some change here }); <module2::ModuleStorage2<T>>::mutate(&key2, |val| { // some change here }); ... } ``` And you have to import module1 and module2 to your current cargo.toml. ```toml module1 = { path = ".." } module2 = { path = ".." } ``` ## How to decouple ### Senario: There's a pallet named `Exchange` that wants to read/write pallet `Token`’s storage. `Token` has a storage: ```rust= decl_storage! { trait Store for Module<T: Trait> as Token { pub AccountToken get(fn token): map hasher(blake2_128_concat) (T::AccountId, Symbol) => Token; } } ``` 1. Define a trait(Better do define it in a crate only execept `Token`) ```rust= pub trait TokenTrait<AccountId> { fn decrease(who: &AccountId, symbol: Symbol, amount: u128); fn increase(who: &AccountId, symbol: Symbol, amount: u128); fn get_token(who: &AccountId, symbol: Symbol) -> Token; fn has_token(who: &AccountId, symbol: Symbol) -> bool; } ``` 2. Impl this trait for module `Token` ```rust= // impl this trait for others module can access and modyfy token'data impl<T: Trait> token_primitives::TokenTrait<T::AccountId> for Module<T> { fn decrease(who: &T::AccountId, symbol: Symbol, amount: u128) { <AccountToken<T>>::mutate((who, symbol), |token| { token.balance = token.balance.saturating_sub(amount); }); } fn increase(who: &T::AccountId, symbol: Symbol, amount: u128) { <AccountToken<T>>::mutate((who, symbol), |token| { token.balance = token.balance.saturating_add(amount); }); } fn get_token(who: &T::AccountId, symbol: Symbol) -> Token { <AccountToken<T>>::get((who, symbol)) } fn has_token(who: &T::AccountId, symbol: Symbol) -> bool { <AccountToken<T>>::contains_key((who, symbol)) } } ``` 3. Register a trait handler for module `Exchange`. ```rust= use token_primitives::{Symbol, TokenTrait}; pub trait Trait: frame_system::Trait { type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>; /// Token trait handler type TokenTrait: TokenTrait<Self::AccountId>; } // usage ensure!( T::TokenTrait::has_token(&exchanger, exchange_symbol) && T::TokenTrait::has_token(&exchanger, target_symbol), Error::<T>::UserHasNoThisToken ); ensure!( T::TokenTrait::get_token(&exchanger, exchange_symbol).balance >= amount, Error::<T>::ExchangeTooMuch ); ``` 4. Connect them in runtime ```rust= // configure token for runtime impl token::Trait for Runtime { type Event = Event; } // configure token exchange for runtime impl token_exchange::Trait for Runtime { type Event = Event; type TokenTrait = Token; // this is the key for decoupling modules } construct_runtime!( pub enum Runtime where Block = Block, NodeBlock = node_primitives::Block, UncheckedExtrinsic = UncheckedExtrinsic { … // construct token and exchange fo runtime Token: token::{Pallet, Call, Storage, Event<T>}, TokenExchange: token_exchange::{Pallet, Call, Storage, Event<T>}, } ); ```