3

I'm trying to design a custom pallet with bounded vectors, but I want to leave the user to define the size of the bounds according to their implementation, so I use the next type:

    pub trait Config: frame_system::Config + pallet_aura::Config {
        ...
        
        #[pallet::constant]
        type MaxDelegations: Get<u32>;
        #[pallet::constant]
        type MaxAuthorities: Get<u32>;
    }

And I use them in the next struct:

    pub struct DelegatorConfig<T: Config, AccountId, Balance> {
        pub is_validator: bool,
        pub delegated_points: Balance,
        pub delegates: BoundedVec<(AccountId, Balance), T::MaxDelegations>,
    }

And this storage

#[pallet::storage]
    pub type Delegators<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::AccountId,
        DelegatorConfig<T::AccountId, BalanceOf<T>, T::MaxDelegations>,
    >;

But now the compilers sends me this error message, what didn't happen when I just used Const<U32> for the bound. Any idea what I am missing? I'm pasting the full code next:

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

/// Edit this file to define custom logic or remove it if it is not needed.
/// Learn more about FRAME and the core library of Substrate FRAME pallets:
/// <https://docs.substrate.io/reference/frame-pallets/>
pub use pallet::*;

#[cfg(test)]
mod mock;

#[cfg(test)]
mod tests;

#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;

#[frame_support::pallet]
pub mod pallet {
    use super::*;
    use frame_support::bounded_vec;
    use frame_support::pallet_prelude::DispatchResult;
    use frame_support::traits::tokens::Balance;
    use frame_support::traits::{LockIdentifier, WithdrawReasons};
    use frame_support::{
        pallet_prelude::*,
        traits::{Currency, FindAuthor, LockableCurrency, ReservableCurrency},
    };
    use frame_system::pallet_prelude::{OriginFor, *};
    use sp_consensus_aura::{Slot, AURA_ENGINE_ID};
    use sp_runtime::traits::Convert;
    use sp_runtime::traits::{CheckedAdd, CheckedSub};

    pub type BalanceOf<T> =
        <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

    const LOCK_ID: LockIdentifier = *b"1       ";

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

    /// Configure the pallet by specifying the parameters and types on which it depends.
    #[pallet::config]
    pub trait Config: frame_system::Config + pallet_aura::Config {
        /// Because this pallet emits events, it depends on the runtime's definition of an event.
        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

        /// Type to access the Balances Pallet.
        type Currency: Currency<Self::AccountId>
            + ReservableCurrency<Self::AccountId>
            + LockableCurrency<Self::AccountId>;

        /// A conversion function which takes an authority id, and returns the associated account id.
        type AuthorityToAccount: Convert<Self::AuthorityId, Self::AccountId>;

        type ValidatorBond: Get<BalanceOf<Self>>;
        
        #[pallet::constant]
        type MaxDelegations: Get<u32>;
        #[pallet::constant]
        type MaxAuthorities: Get<u32>;
    }

    // Pallets use events to inform users when important changes are made.
    // https://docs.substrate.io/main-docs/build/events-errors/
    #[pallet::event]
    #[pallet::generate_deposit(pub(super) fn deposit_event)]
    pub enum Event<T: Config> {}

    // Errors inform users that something went wrong.
    #[pallet::error]
    pub enum Error<T> {
        /// Too many authorities for Aura's limits.
        TooManyAuthorities,
    }

    #[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Debug, Eq, PartialEq, Clone)]
    pub struct ValidatorConfig<T: Config, AccountId, Balance> {
        pub is_active: bool,
        pub points: Balance,
        pub delegators: BoundedVec<(AccountId, Balance), T::MaxDelegations>,
    }

    #[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Debug, Eq, PartialEq, Clone)]
    pub struct DelegatorConfig<T: Config, AccountId, Balance> {
        pub is_validator: bool,
        pub delegated_points: Balance,
        pub delegates: BoundedVec<(AccountId, Balance), T::MaxDelegations>,
    }

    #[derive(
        Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen,
    )]
    pub struct TotalStake<Balance> {
        pub validators: Balance,
    }

    #[derive(Encode, Decode, TypeInfo, MaxEncodedLen, Debug, Eq, PartialEq, Clone)]
    pub struct Candidates<T: Config, AccountId, Balance> {
        pub authorities: BoundedVec<(AccountId, Balance),  <T as pallet::Config>::MaxAuthorities>,
    }

    #[pallet::storage]
    pub type Validators<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::AccountId,
        ValidatorConfig<T::AccountId, BalanceOf<T>, T::MaxDelegations>,
    >;

    #[pallet::storage]
    pub type Delegators<T: Config> = StorageMap<
        _,
        Blake2_128Concat,
        T::AccountId,
        DelegatorConfig<T::AccountId, BalanceOf<T>, T::MaxDelegations>,
    >;

    #[pallet::storage]
    pub type StakePool<T: Config> = StorageValue<_, TotalStake<BalanceOf<T>>, ValueQuery>;

    #[pallet::storage]
    pub type EnabledAuthorities<T: Config> =
        StorageValue<_, Candidates<T::AccountId, BalanceOf<T>,  <T as pallet::Config>::MaxAuthorities>, OptionQuery>;

    // Dispatchable functions allows users to interact with the pallet and invoke state changes.
    // These functions materialize as "extrinsics", which are often compared to transactions.
    // Dispatchable functions must be annotated with a weight and must return a DispatchResult.
    #[pallet::call]
    impl<T: Config> Pallet<T> {
        /// An example of directly updating the authorities for Aura.
        #[pallet::call_index(0)]
        #[pallet::weight(0)]
        pub fn force_change_authorities(
            origin: OriginFor<T>,
            who: T::AuthorityId,
        ) -> DispatchResult {
            ensure_root(origin)?;
            let mut authorities = BoundedVec::<T::AuthorityId, T::MaxAuthorities>::default();
            authorities.try_push(who).map_err(|_| Error::<T>::TooManyAuthorities)?;
            pallet_aura::Pallet::<T>::change_authorities(authorities);
            Ok(())
        }

        #[pallet::call_index(1)]
        #[pallet::weight(0)]
        pub fn register_validator(origin: OriginFor<T>) -> DispatchResult {
            let who = ensure_signed(origin)?;

            let default_config = ValidatorConfig {
                is_active: true,
                points: Default::default(),
                delegators: bounded_vec![],
            };

            // reserve some balance from the user
            T::Currency::reserve(&who, T::ValidatorBond::get())?;

            Validators::<T>::insert(who.clone(), default_config);
            Pallet::<T>::check_candidate(who);

            Ok(())
        }

        #[pallet::call_index(2)]
        #[pallet::weight(0)]
        pub fn delegate(
            origin: OriginFor<T>,
            validator: T::AccountId,
            new_amount: BalanceOf<T>,
        ) -> DispatchResult {
            // THERE ARE ERRORS HERE. DONT USE DIRECTLY. THINK ABOUT MULTI DELEGATION SCENARIO

            let who = ensure_signed(origin)?;
            let amount = new_amount.clone();
            let mut validator_config = Validators::<T>::get(&validator).ok_or("not a validator")?;

            let mut delegator_config;

            if Delegators::<T>::get(&who).is_some() {
                delegator_config = Delegators::<T>::get(&who).unwrap();
            } else {
                Pallet::<T>::register_delegator(who.clone());
                delegator_config = Delegators::<T>::get(&who).unwrap();
            }

            let new_delegation_amount =
                delegator_config.delegated_points.checked_add(&new_amount).unwrap();

            ensure!(T::Currency::free_balance(&who) >= new_amount, "not enough funds");
            T::Currency::set_lock(LOCK_ID, &who, new_amount, WithdrawReasons::all());

            ensure!(!validator_config.delegators.is_full(), "overflow");

            let pos_val = validator_config.delegators.iter().position(|(x, _)| x == &who);
            delegator_config.delegated_points = new_delegation_amount;
            validator_config.points += amount;
            if pos_val != None {
                validator_config.delegators[pos_val.unwrap()].1 += amount;
            } else {
                validator_config.delegators.try_push((who.clone(), amount));
            }
            let pos_del = delegator_config.delegates.iter().position(|(x, _)| x == &validator);
            if pos_del != None {
                delegator_config.delegates[pos_del.unwrap()].1 += amount;
            } else {
                delegator_config.delegates.try_push((validator.clone(), amount));
            }

            if Validators::<T>::contains_key(validator.clone()) {
                Validators::<T>::remove(validator.clone());
                Validators::<T>::insert(validator.clone(), validator_config);
            } else {
                Validators::<T>::insert(validator.clone(), validator_config);
            }

            if Delegators::<T>::contains_key(who.clone()) {
                Delegators::<T>::remove(who.clone());
                Delegators::<T>::insert(who.clone(), delegator_config);
            } else {
                Delegators::<T>::insert(who.clone(), delegator_config);
            }

            Pallet::<T>::increase_stake(amount);

            Pallet::<T>::check_candidate(validator);

            Ok(())
        }

        #[pallet::call_index(3)]
        #[pallet::weight(0)]
        pub fn revoke_delegate(
            origin: OriginFor<T>,
            validator: T::AccountId,
            amount: BalanceOf<T>,
        ) -> DispatchResult {
            let who = ensure_signed(origin)?;
            let other_who = who.clone();
            let mut validator_config = Validators::<T>::get(&validator).ok_or("not a validator")?;

            let mut delegator_config = Delegators::<T>::get(&who).unwrap();

            ensure!(
                !(validator_config.delegators.iter().find(|(x, _)| x == &who) == None),
                "not a delegator of the current validator"
            );
            let pos_val = validator_config.delegators.iter().position(|(x, _)| x == &who).unwrap();
            let validator_points = validator_config.points;
            let validator_delegator_points = validator_config.delegators[pos_val].1;
            ensure!(validator_delegator_points >= amount, "not enough delegated points");
            let points_to_remove = std::cmp::min(amount, validator_delegator_points);
            ensure!(validator_config.is_active, "validator currently inactive");
            validator_config.points =
                validator_config.points.checked_sub(&points_to_remove).ok_or("underflow")?;

            let pos_del =
                delegator_config.delegates.iter().position(|(x, _)| x == &validator).unwrap();

            let points_remaining = validator_delegator_points - points_to_remove;
            if validator_delegator_points == points_to_remove {
                validator_config.delegators.remove(pos_val);
                delegator_config.delegates.remove(pos_del);
            }

            let new_delegation_amount = delegator_config.delegated_points - points_to_remove;
            delegator_config.delegated_points = new_delegation_amount;
            T::Currency::remove_lock(LOCK_ID, &who);
            T::Currency::set_lock(LOCK_ID, &who, new_delegation_amount, WithdrawReasons::all());

            Validators::<T>::insert(validator.clone(), validator_config);
            Delegators::<T>::insert(who.clone(), delegator_config);
            Pallet::<T>::reduce_stake(amount);
            Pallet::<T>::check_candidate(validator);

            Ok(())
        }
    }

    impl<T: Config> Pallet<T> {
        // A function to get you an account id for the current block author.
        fn find_author() -> Option<T::AccountId> {
            let digest = frame_system::Pallet::<T>::digest();
            let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());

            for (id, mut data) in pre_runtime_digests {
                if id == AURA_ENGINE_ID {
                    let slot = Slot::decode(&mut data).ok()?;
                    let author_index = *slot % pallet_aura::Pallet::<T>::authorities().len() as u64;
                    let validators = pallet_aura::Pallet::<T>::authorities();
                    let authority = validators.get(author_index as usize).cloned()?;
                    return Some(T::AuthorityToAccount::convert(authority));
                }
            }

            None
        }

        fn get_slot() -> Option<sp_consensus_aura::Slot> {
            let digest = frame_system::Pallet::<T>::digest();
            let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());

            for (id, mut data) in pre_runtime_digests {
                if id == AURA_ENGINE_ID {
                    let slot = Slot::decode(&mut data).ok()?;
                    return Some(slot);
                }
            }

            None
        }

        fn increase_stake(amount: BalanceOf<T>) {
            let mut stake;
            match StakePool::<T>::try_get() {
                Ok(x) => {
                    stake = x;
                    stake.validators += amount;
                    StakePool::<T>::set(stake);
                },
                Err(_) => {
                    stake = TotalStake { validators: amount };
                    stake.validators = amount;
                    StakePool::<T>::set(stake);
                },
            }
        }

        fn reduce_stake(amount: BalanceOf<T>) {
            let mut stake;
            match StakePool::<T>::try_get() {
                Ok(x) => {
                    stake = x;
                    stake.validators -= amount;
                    StakePool::<T>::set(stake);
                },
                Err(_) => {
                    stake = TotalStake { validators: Default::default() };
                    StakePool::<T>::set(stake);
                },
            };
        }

        fn check_candidate(candidate: T::AccountId) -> DispatchResult {
            let mut candidates = EnabledAuthorities::<T>::get();

            match candidates {
                Some(x) => {
                    let mut candidates = x;
                    candidates.authorities.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap());
                    let candidate_balance =
                        Validators::<T>::get(&candidate).ok_or("not a validator")?;
                    match candidate_balance.points > candidates.authorities[0].1 {
                        true => match candidate == candidates.authorities[0].0 {
                            true => {
                                candidates.authorities[0].1 = candidate_balance.points;
                                EnabledAuthorities::<T>::set(Some(candidates));
                            },
                            false => {
                                candidates.authorities[0] = (candidate, candidate_balance.points);
                                EnabledAuthorities::<T>::set(Some(candidates));
                            },
                        },
                        false => match candidate == candidates.authorities[0].0 {
                            true => {
                                candidates.authorities[0].1 = candidate_balance.points;
                                EnabledAuthorities::<T>::set(Some(candidates));
                            },
                            false => {
                                let mut pos = candidates
                                    .authorities
                                    .iter()
                                    .position(|(x, _)| *x == candidate);
                                match pos {
                                    Some(p) => {
                                        candidates.authorities.remove(p);
                                        EnabledAuthorities::<T>::set(Some(candidates));
                                    },
                                    None => (),
                                }
                            },
                        },
                    }
                },

                None => {
                    let candidate_balance =
                        Validators::<T>::get(&candidate).ok_or("not a validator")?;
                    let mut candidates = Candidates { authorities: Default::default() };
                    let tuple = (candidate, candidate_balance.points);
                    candidates.authorities.try_push(tuple);
                    EnabledAuthorities::<T>::set(Some(candidates));
                },
            }

            Ok(())
        }

        fn register_delegator(who: T::AccountId) -> DispatchResult {
            let default_config = DelegatorConfig {
                is_validator: false,
                delegated_points: Default::default(),
                delegates: bounded_vec![],
            };

            Delegators::<T>::insert(who, default_config);

            Ok(())
        }
    }
}

Oh and this is the full error message:

warning: unused import: `frame_support::traits::tokens::Balance`
  --> pallets/dpos/src/lib.rs:22:6
   |
22 |     use frame_support::traits::tokens::Balance;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default

warning: unused import: `FindAuthor`
  --> pallets/dpos/src/lib.rs:26:22
   |
26 |         traits::{Currency, FindAuthor, LockableCurrency, ReservableCurrency},
   |                            ^^^^^^^^^^

error[E0277]: the trait bound `<T as frame_system::Config>::AccountId: pallet::Config` is not satisfied
   --> pallets/dpos/src/lib.rs:39:44
    |
39  |       #[pallet::generate_store(pub(super) trait Store)]
    |  _______________________________________________^
40  | |     pub struct Pallet<T>(_);
41  | |
42  | |     /// Configure the pallet by specifying the parameters and types on which it depends.
...   |
103 | |     #[pallet::storage]
104 | |     pub type Validators<T: Config> = StorageMap<
    | |_______________________^ the trait `pallet::Config` is not implemented for `<T as frame_system::Config>::AccountId`
    |
note: required by a bound in `ValidatorConfig`
   --> pallets/dpos/src/lib.rs:78:32
    |
78  |     pub struct ValidatorConfig<T: Config, AccountId, Balance> {
    |                                   ^^^^^^ required by this bound in `ValidatorConfig`
help: consider further restricting the associated type
    |
39  |     #[pallet::generate_store(pub(super) trait Store where <T as frame_system::Config>::AccountId: pallet::Config)]
    |                                                     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0277]: the trait bound `<T as frame_system::Config>::AccountId: pallet::Config` is not satisfied
   --> pallets/dpos/src/lib.rs:39:44
    |
39  |       #[pallet::generate_store(pub(super) trait Store)]
    |  _______________________________________________^
40  | |     pub struct Pallet<T>(_);
41  | |
42  | |     /// Configure the pallet by specifying the parameters and types on which it depends.
...   |
111 | |     #[pallet::storage]
112 | |     pub type Delegators<T: Config> = StorageMap<
    | |_______________________^ the trait `pallet::Config` is not implemented for `<T as frame_system::Config>::AccountId`
    |
note: required by a bound in `DelegatorConfig`
   --> pallets/dpos/src/lib.rs:85:32
    |
85  |     pub struct DelegatorConfig<T: Config, AccountId, Balance> {
    |                                   ^^^^^^ required by this bound in `DelegatorConfig`
help: consider further restricting the associated type
    |
39  |     #[pallet::generate_store(pub(super) trait Store where <T as frame_system::Config>::AccountId: pallet::Config)]
    |                                                     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0277]: the trait bound `<T as frame_system::Config>::AccountId: pallet::Config` is not satisfied
   --> pallets/dpos/src/lib.rs:39:44
    |
39  |       #[pallet::generate_store(pub(super) trait Store)]
    |  _______________________________________________^
40  | |     pub struct Pallet<T>(_);
41  | |
42  | |     /// Configure the pallet by specifying the parameters and types on which it depends.
...   |
122 | |     #[pallet::storage]
123 | |     pub type EnabledAuthorities<T: Config> =
    | |_______________________________^ the trait `pallet::Config` is not implemented for `<T as frame_system::Config>::AccountId`
    |
note: required by a bound in `Candidates`
   --> pallets/dpos/src/lib.rs:99:27
    |
99  |     pub struct Candidates<T: Config, AccountId, Balance> {
    |                              ^^^^^^ required by this bound in `Candidates`
help: consider further restricting the associated type
    |
39  |     #[pallet::generate_store(pub(super) trait Store where <T as frame_system::Config>::AccountId: pallet::Config)]
    |                                                     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
warning: `pallet-dpos` (lib) generated 2 warnings
error: could not compile `pallet-dpos` due to 3 previous errors; 2 warnings emitted```
Thanks

2 Answers 2

2

You are using DelegatorConfig<T::AccountId, BalanceOf<T>, T::MaxDelegations> instead of DelegatorConfig<T, T::AccountId, BalanceOf<T>>.

2
  • Yeah, that helps but if you look at the full code I use #[derive(TypeInfo)], which doesn't let me apply your solution in particular. But it did help, thanks!
    – be344
    Jan 28, 2023 at 21:44
  • 1
    My rep doesn't allow me to vote yet, but you helped so you got a spiritual upvote from me
    – be344
    Jan 28, 2023 at 21:48
1

So what I did was the following: addded #[scale_info(skip_type_params(T))] to the struct and modified the rest as follows:

    #[scale_info(skip_type_params(T))]
    pub struct DelegatorConfig<T: Config> {
        pub is_validator: bool,
        pub delegated_points: BalanceOf<T>,
        pub delegates: BoundedVec<(T::AccountId, BalanceOf<T>), T::MaxDelegations>,
    }
#[pallet::storage]
    pub type Delegators<T: Config> = StorageMap<
    _,
    Blake2_128Concat,
    T::AccountId,
    DelegatorConfig<T>,
    >;

Your Answer

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

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