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