Currently, the maximum number of nominations per nominator is set to 16 in Polkadot. This constant limit was historically imposed for scalability reasons, mostly because staking needs to calculate and store a snapshot of the nominations graph on-chain before each election. Too many nominations per nominator will yield a larger graph and storage being used per election.
We recently merged a new feature in staking that enables a dynamic quota of nominations per nominator [1] based on the nominator’s locked balance for staking. This basically means that we can implement a curve that given a nominator’s balance, calculates the nomination quota. The quota can be lower or higher than the current max number of nominations in Polkadot.
Another feature that we introduced in the PR is the ability to bound the snapshot by both number of total nominators (edges) and size of the snapshot. As the staking pallet is constructing the snapshot for the election, it keeps track of the i) number of nominators in the snapshot and the ii) current (encoded) size of the snapshot. Once one of the imposed thresholds is exceeded (or all the nominations included in the snapshot), the snapshot builder stops processing nominations and stores the snapshot in the storage for the staking miners to fetch and calculate an election solution off-chain.
The current curve implementation is constant [2] , which maps to the max nominations (16 in Polkadot, 24 in Kusama) that has been historically set. This means that we’re not tapping into the potential of the dynamic number of nominations yet. In this post, I’d like to open the discussion to the community of what would be a reasonable nomination quota curve for Polkadot and Kusama.
But first, I’d like to expand on the consequences of changing the nomination quota on a nominator basis (see examples below for more details on why this is the case):
- Potentially lower the minimum active stake by increasing the number of nominators exposed to each election, while keeping a reasonable snapshot size;
- Increase the total stake in the snapshot and (potentially) the stake behind the election result (more economic security), since there’s more stake to be distributed by Phragmen.
Examples: Assuming that a snapshot size can fit up to 12 nominations (edges) for the whole election (enforced by the encoded size snapshot limits).
Example 1. max_nominations
are fixed at 6 nominations/nominator:
nominator_1
stakes 6 DOT, nominates 6 validators
nominator_2
stakes 4 DOT, nominates 4 validators
nominator_3
stakes 4 DOT, nominates 4 validators
nominator_4
stakes 2 DOT, nominates 3 validators
the final snapshot will have: [nominator_1
, nominator_2
, nominator_3
], which means:
min_active_stake
= 4 DOTtotal_stake
exposed to next election (but not necessarily in the final active set, that depends on the election): 14 DOT
Example 2. max_nominations
curve is: 6 nominations if staked balance > 5, otherwise 2 nominations max.
nominator_1
stakes 6 DOT, nominates 6 validators
nominator_2
stakes 4 DOT, nominates 2 validators
nominator_3
stakes 4 DOT, nominates 2 validators
nominator_4
stakes 2 DOT, nominates 2 validators
the final snapshot will have: [nominator_1
, nominator_2
, nominator_3
, nominator_4
], which means:
min_active_stake
= 2 DOT
total_stake
exposed to next election (but not necessarily in the final active set, that depends on the election): 16 DOT
Other considerations:
One of the advantages of NPoS with multiple nominations is that is is risk-free to nominate new validators that are bootstrapping, since the total stake of the nominator will be exposed to rewards if at least one of its nominated validator is selected. We should keep this in mind and do not place overly strict rules (e.g. allow only one nomination unless nominator has more than 2x min_active_stake
of last election, for example) that would be overall counter productive.
Lazy nomination quota checks: an implementation detail to keep in mind is that the nominations quota is only imposed when Call::nominate
is called with a new set of nominations. This means that old nominations already stored may exceed their quota. We expect that eventually the discrepancy will decrease.
Impact to wallets/dapps: Wallets and staking apps will want to predict what is the nominations quota for a given nominator so that the user can select their nominations up to the assigned quota (note: call to Call::nominate
will fail if the number of targets is above the nominator’s quota). There’s a runtime API that exposes the nomination quota for a given balance [3], so that wallets can predict the nomination quota and use that information in the wallet’s UX. Before we enable the dynamic nominations quota, we’ll make sure that dapps integrating with staking are aware of the required changes on their side.
In sum, this post is a call to the community to engage and discuss what would be a reasonable nominations quota curve implementation [4].
[1] Allow for a dynamic number of nominators · Issue #12968 · paritytech/substrate · GitHub
[2] https://github.com/polkadot-fellows/runtimes/blob/c86afcc9ef185465df30878e4415cf6d8212fcd6/relay/polkadot/src/lib.rs#L590C5-L590C5
[3] Runtime | polkadot{.js}
[4] NominationsQuota in pallet_staking - Rust