Information Gathering
There are so many parachains and sovereign networks in the Polkadot ecosystem. How should we, as operators, pick which networks to operate for? Information gathering, like figuring out which networks are already saturated, and understanding the overall landscape is vital to making informed decisions.
Terminology
BP / block producer
An umbrella term referring to either collator or validator, for the sake of convenience.
waiting, num_waiting
A candidate is waiting once registered and yet to be selected/elected. It is not participating in BP activities like producing, validating or voting for blocks.
active, num_active
When a candidate is selected (among the waiting ones), it becomes active. An active candidate is given the permission and is expected to produce blocks (and earn rewards). Failing to do so may face penalties against the stake.
invulnerable (only on parachains)
When a candidate is set invulnerable by a privileged entity, it has a secured slot as a collator every session. It exists to guarantee uninterrupted block production regardless of the state of the candidate pool.
The active set and the invulnerable set are mutually exclusive. Not every parachain has an invulnerable set.
max_slots
The predefined hard cap on the number of candidates to be selected as BPs.
On parachains, the number does not include the invulnerable ones. That is to say, the number of collators on a fully loaded parachain is the sum of the number of invulnerable candidates and max_slots.
candidacy_bond
The minimum self-bond required to register as a BP candidate.
candidacy_stake
The minimum total stake required to be selected/elected as a BP. It is composed self-bond and delegated stake.
Prerequisites
Most of the numbers being used in the ecosystem, like balances, are of type BN.
/**
* Convert balance into the number of native tokens.
* @param {BN|number} balance
* @return {number}
*/
function toNative(balance) {
// [0] represents the decimal precision of the native token.
// The rest are precisions of the other transferrable tokens on the chain.
const decimals = api.registry.chainDecimals[0]
const unit = new BN(10).pow(new BN(decimals))
const divmod = new BN(balance).divmod(unit)
return parseFloat(`${divmod.div}.${divmod.mod}`)
}
function toNativeInt(balance) {
balance = balance instanceof BN ? balance.toBigInt() : BigInt(balance)
const decimals = api.registry.chainDecimals[0]
return Number(balance / (10n ** BigInt(decimals)))
}Collator
In general, parachains that allow delegating stake to collators use the parachain-staking pallet for collator selection. Others that don’t often use collator-selection. Very rarely, a parachain may use both.
Parachain developers have complete control over the collator selection logic. Not every parachain uses parachain-staking or collator-selection. Even when they do, there is no guarantee that they will stick to the original interfaces, so the snippets below do not always work.
parachainStaking
The original pallet is maintained by Moonbeam and adopted by parachains with similar functionalities.
max_slots
(await api.query.parachainStaking.totalSelected()).toNumber()num_active
(await api.query.parachainStaking.selectedCandidates()).toHuman().lengthnum_waiting
(await api.query.parachainStaking.candidatePool()).toHuman().length - num_activecandidacy_bond
toNative(api.consts.parachainStaking.minCandidateStk)
// occasionally
toNative(api.consts.parachainStaking.minCollatorCandidateStake)
// DEPRECATED API for something similar
api.consts.parachainStaking.minCollatorStkcandidacy_stake
const candidatePool = await api.query.parachainStaking.candidatePool()
num_active < max_slots
? minCollatorStake || candidacy_bond
: candidatePool
.map((c) => toNative(c.amount))
.sort((a, b) => b - a)[max_slots - 1]collatorSelection
The original pallet is maintained by Parity .
max_slots
(await api.query.collatorSelection.desiredCandidates()).toNumber()num_active
function candidateList() {
// candidates() is DEPRECATED in favor of candidateList()
// but some chains are still using legacy versions of the pallet
return typeof api.query.collatorSelection.candidateList === 'function'
? api.query.collatorSelection.candidateList()
: api.query.collatorSelection.candidates()
}
Math.min(max_slots, (await candidateList()).toHuman().length)num_waiting
Math.max(max_slots, (await candidateList()).toHuman().length) - max_slotscandidacy_bond
toNative(await api.query.collatorSelection.candidacyBond())