I'm currently integrating staking functionality into my pallet. Here is a snipped of the relevant part:

let who = ensure_signed(origin.clone())?;
let required_stake = T::RequiredStake::get();
let controller: <<T as frame_system::Config>::Lookup as StaticLookup>::Source =
let payee = RewardDestination::Staked;
<pallet_staking::Pallet<T>>::bond(origin, controller, required_stake, payee)?;

where T::RequiredStake is just some amount of balance, so this all compiles fine.

The abbreviated test runtime looks like this:

    pub enum TestRuntime where
        Block = Block,
        NodeBlock = Block,
        UncheckedExtrinsic = UncheckedExtrinsic,
        System: frame_system,
        Timestamp: pallet_timestamp,
        Balances: pallet_balances,
        VoterList: pallet_bags_list,
        Staking: pallet_staking,
        ShelfCommittees: crate,

impl pallet_staking::Config for TestRuntime {
    type Currency = Balances;
    type CurrencyBalance = Balance;
    type UnixTime = Timestamp;
    type CurrencyToVote = U128CurrencyToVote;
    type GenesisElectionProvider = NoElection<(AccountId, BlockNumber, Staking, ())>;
    type ElectionProvider = NoElection<(AccountId, BlockNumber, Staking, ())>;
    type MaxNominations = MaxNominations;
    type RewardRemainder = ();
    type RuntimeEvent = RuntimeEvent;
    type Slash = ();
    type Reward = ();
    type SessionsPerEra = SessionsPerEra;
    type BondingDuration = BondingDuration;
    type SlashDeferDuration = SlashDeferDuration;
    type SessionInterface = ();
    type EraPayout = ();
    type NextNewSession = ();
    type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
    type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
    type TargetList = UseValidatorsMap<Self>;
    type MaxUnlockingChunks = ConstU32<32>;
    type HistoryDepth = frame_support::traits::ConstU32<84>;
    type BenchmarkingConfig = StakingBenchmarkingConfig;
    type WeightInfo = pallet_staking::weights::SubstrateWeight<TestRuntime>;
    type VoterList = VoterList;
    type OnStakerSlash = ();
    type AdminOrigin = EnsureRoot<AccountId>;


When testing with a mock runtime I get the following error (abbreviated):

panicked at 'called `Result::unwrap()` on an `Err` value: Module(ModuleError { index: 4, error: [17, 0, 0, 0], message: Some("BadState") })'

To me this is not clear what the error is supposed to represent. I would have expected the staking pallet to throw some other error (e.g not enough free balance or similar), but not this.

Is there maybe some GenesisConfig I need to set for the staking pallet?

Looking forward to hearing your thoughts.

  • Can you share the runtime for your pallet as well. I think this might not be the ideal way you call another pallet. Instead include pallet_staking traits into the Config of your pallet. Then integrate both in your mock runtime and then call it. Jul 11, 2023 at 6:20
  • I added the test runtime configuration in the post Jul 11, 2023 at 8:47

1 Answer 1


So it turns out that the bond extrinsic of pallet-staking is attempting to call inc_consumers of frame-system, which checks if the accounts providers is greater than 0. If this is not the case then NoProviders is returned, which the staking pallet maps to BadState which is a pretty bad error message IMO.

So making sure that providers for the account is at least 1 does the trick. This can be done by calling <frame_system::Pallet<T>>::inc_providers(who).

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.