pallet-fast-unstake 32.0.0

FRAME fast unstake pallet (polkadot v1.11.0)
Documentation

Made with Substrate, for Polkadot.

github - 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

  1. Fast-unstake with multiple participants in the queue.

  2. 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 used on_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.