Made with Substrate, for Polkadot.
Fast Unstake Pallet
A pallet to allow participants of the staking system (represented by [Config::Staking
], being
[StakingInterface
]) to unstake quicker, if and only if they meet the condition of not being
exposed to any slashes.
Overview
If a nominator is not exposed anywhere in the staking system, checked via
[StakingInterface::is_exposed_in_era
] (i.e. "has not actively backed any validators in the
last [StakingInterface::bonding_duration
] days"), then they can register themselves in this
pallet and unstake faster than having to wait an entire bonding duration.
Being exposed with validator from the point of view of the staking system means earning rewards with the validator, and also being at the risk of slashing with the validator. This is equivalent to the "Active Nominator" role explained in here.
Stakers who are certain about NOT being exposed can register themselves with
[Pallet::register_fast_unstake
]. This will chill, fully unbond the staker and place them
in the queue to be checked.
A successful registration implies being fully unbonded and chilled in the staking system. These
effects persist even if the fast-unstake registration is retracted (see [Pallet::deregister
]
and further).
Once registered as a fast-unstaker, the staker will be queued and checked by the system. This can take a variable number of blocks based on demand, but will almost certainly be "faster" (as the name suggest) than waiting the standard bonding duration.
A fast-unstaker is either in [Queue
] or actively being checked, at which point it lives in
[Head
]. Once in [Head
], the request cannot be retracted anymore. But, once in [Queue
], it
can, via [Pallet::deregister
].
A deposit equal to [Config::Deposit
] is collected for this process, and is returned in case a
successful unstake occurs (Event::Unstaked
signals that).
Once processed, if successful, no additional fee for the checking process is taken, and the staker is instantly unbonded.
If unsuccessful, meaning that the staker was exposed, the aforementioned deposit will be slashed for the amount of wasted work they have inflicted on the chain.
All in all, this pallet is meant to provide an easy off-ramp for some stakers.
Example
-
Fast-unstake with multiple participants in the queue.
-
Fast unstake failing because a nominator is exposed.
Pallet API
See the [pallet
] module for more information about the interfaces this pallet exposes,
including its configuration trait, dispatchables, storage items, events and errors.
Low Level / Implementation Details
This pallet works off the basis of on_idle
, meaning that it provides no guarantee about when
it will succeed, if at all. Moreover, the queue implementation is unordered. In case of
congestion, no FIFO ordering is provided.
A few important considerations can be concluded based on the on_idle
-based implementation:
-
It is crucial for the weights of this pallet to be correct. The code inside [
Pallet::on_idle
] MUST be able to measure itself and report the remaining weight correctly after execution. -
If the weight measurement is incorrect, it can lead to perpetual overweight (consequently slow) blocks.
-
The amount of weight that
on_idle
consumes is a direct function of [ErasToCheckPerBlock
]. -
Thus, a correct value of [
ErasToCheckPerBlock
] (which can be set via [Pallet::control
]) should be chosen, such that a reasonable amount of weight is usedon_idle
. If [ErasToCheckPerBlock
] is too large,on_idle
will always conclude that it has not enough weight to proceed, and will early-return. Nonetheless, this should also be safe as long as the benchmarking/weights are accurate. -
See the inline code-comments on
do_on_idle
(private) for more details. -
For further safety, in case of any unforeseen errors, the pallet will emit [
Event::InternalError
] and set [ErasToCheckPerBlock
] back to 0, which essentially means the pallet will halt/disable itself.