The purpose of the staking token collective is to enable multiple individual users or users with a specific governance role/s to lock & combine together their staking derivative tokens in order to jointly claim the staking rewards and redistribute them to another (distinct) set of users. In simple words, we are allowing a set of people to own/control the staking tokens and yet another distinct set/s of people to earn the rewards from those tokens being staked.
Staking Collectives can be used by self-governed and self-organized active community members whose goal is to build their own incentivization structures to execute various off-chain community goals and missions. The Staking Collectives will be distributing rewards through Spending Pools, which will be modified to support the distribution of multiple tokens.
Objectives
Collective control via proposals (multi-sig like) of staking rewards and donations
Locking of staking tokens within a collective for a defined time period.
Equal or Weighted distribution of the staking and/or UBI rewards
Implementation
I. Collective Creation
Create a registrar in which ANY user/account (without the need to have a dedicated role or permission) can create a Staking Collective. For the collective to become activated a minimum bond amount of staking tokens or derivative V<ID>_tokens will have to be committed to the collective pool, the default min_collective_bond should be equivalent to 100’000 KEX and configurable in the Network Properties (or the equivalent of any other staking token/s as per Token Rates Registrar). If within min_collective_bonding_time (seconds, default 24h - 86400) the min_collective_bond locked is not reached then the collective should be removed from the registrar and the funds returned to the people/person creating the collective.
In order to send a collective creation tx users should commit in the same transaction a minimum of 10% of the min_collective_bond in the form of one or MANY staking tokens. ONLY the staking and their derivative tokens should be allowed to be bonded and the ONLY people who SHOULD be allowed to bond more staking/derivative tokens in the collective creation process should either be an original creator or a person designed by the collective creator in the contributors deposit-whitelist (unless any flag is set to true allowing anyone to contribute their staking rewards to the collective even if they are NOT explicitly whitelisted).
It must be possible to distribute a share of all cumulative rewards to one or MANY spending pools. In the Network Properties max_collective_outputs (default 10) should be defined to limit the maximum number of spending pools. The sum of all share values should be equal to the exact value 1 at all times.
Collective Creation Tx
JSON
Copy
{
"name": "<string>", // collective name - MUST be unique and follow the same rules as councilor or validator moniker)
"description": "<string>", // max 256 characters
"bonds": [ // list of staking DERIVATIVE tokens sent alongside tx (those tokens will be deduced from the user account)
{ "denom": "v3_ukex", "amount": 500000000000 },
{ "denom": "v26_ukex", "amount": 500000000000 },
{ "denom": "v9_samolean", "amount": 20000000000000000000 },
{ ... }, ...
],
"deposit-whitelist": { // deposit-whitelist defines who is allowed to bond tokens
"any": <boolean>, // allow/prevent anyone who is not explicitly specified by role or address to bond to this collective
"roles": [ "<role_name_or_id>" ], // list of specific roles allowed to deposit to this collective
"accounts": [ "<acc_addr_1>", "<acc_addr_2>", ... ] // addresses allowed to deposit to this collective
},
"owners-whitelist": { // list of accounts/roles controlling the collective via “governance-like” proposals
"roles": [ <role-name>, ... ]
"accounts": [ <account-addr>, ... ]
},
"spending-pools": [ // list of spending pools to which share of rewards will be distributed
{
"name": "<unique_name_or_id>", // spending pool to which staking rewards will be sent
"share": <decimal> // A decimal (0-1) defining what percentage of all rewards should be sent to this specific spending pool
}, { ... }, ...
]
"claim-start": <uint>, // (optional) timestamp defining when rewards claiming should start
"claim-period": <uint>, // (optional) period in seconds defining every what period of time reward claim should be triggered
"claim-end": <uint>, // (optional) timestamp defining when rewards claiming should end
"vote-quorum": <percentage>, // default: 51%, collective-specific % of owner accounts that must vote YES or NO for any of the collective proposals to be valid
"vote-period": <seconds>, // default: 600s, period of time in seconds that any of the collective proposals must last before passing or being rejected
"vote-enactment": <seconds>, // default: 300s, period of time that must pass before any of the collective proposals is enacted
}
NOTE: The name of the collective must be unique and NOT possible to change, it can be treated as a unique identifier. We should ensure that no one can send multiple collective creation tx with the same identifier/name or the name that is already used by another collective.
Collective Bonding Tx
It might happen that the initial bond of the individual sending the create tx is insufficient to initialize the collective, for that purpose any account in the deposit-whitelist should be allowed to bond additional tokens to assist the creation of the collective before its initiation time expires (min_collective_bonding_time) and the collective gets removed from the registry without ever becoming active.
There MUST be NO minimum limits in regards to the amount of additional staking derivative tokens that whitelisted individuals can commit to the collective.
JSON
Copy
{
"name": "<string>", // collective name
"bonds": [ // list of STAKING tokens sent alongside tx (those tokens will be deduced from the user account)
{ "denom": "V23_ukex", "amount": 50000000000 },
{ "denom": "v189_samolean", "amount": 10000000000000000000 },]
{ "denom": "v7_samolean", "amount": 80000000000000000000 },
{ ... }, ...
]
}
Bond Locking & Donation Tx
After the collective has been created, all whitelisted contributors should be allowed to voluntarily “lock” their staking derivatives for a defined time period by providing the date (UNIX timestamp) at which ALL deposited tokens can be withdrawn. Once the date is set it should NOT be possible to “unlock” the tokens until that specific date passes. The maximum time period should be NO greater than 1 year from the latest block time OR the latest time the locking & donation transaction was sent. Depositors can always extend the unlock date (indefinitely) but never decrease it. The locking tx can be sent multiple times to increase the locking period (1 year at a time for safety purposes).
In addition to the locking period, whitelisted contributors should be able to configure their individual intended donation to the collective, that is a percentage of rewards (value between 0 and 1) that should be deposited and controlled by the collective. All donations should be subtracted from the amounts being sent to the spending pools. Depositors should also have the ability to “lock” the donation amount using a dedicated donation-lock field until the “locking” period passes. If the locking period is extended the “donation lock” should also persist and remain not changeable.
JavaScript
Copy
{
"name": "<string>", // collective name
"locking": <integer-timestamp>, // time at which deposited tokens will become transferable
"donation": <decimal> // a percentage of rewards that will be re-deposited to the collective
"donation-lock": <boolean> // defines if changing the donation percentage should not be allowed during the locking period
}
II. Collective Structure in the Registry
Collectives should have two types of status, either active, paused or inactive. In the active status, the collective should periodically claim the staking rewards and distribute them to the specified spending pool. In the paused and inactive state, the staking rewards should NOT be claiming or distributing any rewards. After the collective is successfully created its status should be automatically set to inactive and await the collective to delegate the tokens to one or many validators. The status should change to active ONLY when a sufficient amount of tokens are delegated and can be used to claim the rewards (e.g. claim-start elapsed).
Status States Summary
inactive - (default state) The collective is not claiming staking rewards because an insufficient amount of KEX derivatives was deposited or claim-start did not elapse yet
active - The collective has a sufficient amount of staking derivatives deposited by contributors and is claiming rewards as per scheadule (claim start, period, end).
paused - governance decided to “pause” the collection of rewards, only withdrawals are allowed and all deposits to the collective will be rejected.
Claiming of the rewards MUST also start NO earlier than at claim-start date (default - time of collective creation) and occur NOT more often than as defined by a claim-period property. The reward claiming MUST NOT continue once the claim-end date (default 0 - no time limit) is reached. The claim-period (default 86400 - 24h) is defined in seconds and should periodically trigger reward claims to distribute 100% of all rewarded tokens to the spending pool designated by the spending-pool property. Every time rewards are claimed by the staking collective the last-distro property should be updated to mark the time at which the last claim took place. In the Network Properties a reward claim period min_collective_claim_period should be defined (in seconds, default 14400 - 4 hours) which will define the smallest value to which the claim-period can be set.
JSON
Copy
"<collective-name-1>": {
"description": "<string>", // max 256 characters
"status": "active", // defines whether or not a collective should actively collect and distribute rewards or not
"deposit-whitelist": {
"any": <boolean>,
"roles": [ "<role_name_or_id>" ],
"accounts": [ "<acc_addr_1>", "<acc_addr_2>", ... ]
},
"owners-whitelist": { // list of accounts/roles controlling the collective via “governance-like” proposals
"roles": [ <role-name>, ... ]
"accounts": [ <account-addr>, ... ]
},
"spending-pools": [ // list of spending pools to which share of rewards will be distributed
{
"name": "<unique_name_or_id>", // spending pool to which staking rewards will be sent
"share": <decimal> // A decimal (0-1) defining what percentage of all rewards should be sent to this specific spending pool
}, { ... }, ...
]
"claim-start": <uint>, // timestamp defining when rewards claiming should start
"claim-period": <uint>, // period in seconds defining every what period of time a reward claim should be triggered
"claim-end": <uint>, // timestamp defining when rewards claiming should end
"vote-quorum": <percentage>, // default: 51%, collective-specific % of owner accounts that must vote YES or NO for any of the collective proposals to be valid
"vote-period": <seconds>, // default: 600s, period of time in seconds that any of the collective proposals must last before passing or being rejected
"vote-enactment": <seconds>, // default: 300s, period of time that must pass before any of the collective proposals is enacted
"last-distro": <uint>, // timestamp of the block in which tokens were last distributed
"contributors": [ // contributors to the pool collective
{
"address": "<addr>", // string address of the person who deposited tokens
"locking": <timestamp>, // unix timestamp at which tokens can be withdrawn from collective
"donation": <decimal> // a percentage of rewards that will be re-deposited to the collective
"donation-lock": <boolean> // defines if changing the donation percentage should not be allowed during the locking period
"tokens": [ // list of staking tokens deposited by the member
{
"denom": "<token-xyz>", // name of the token (denom)
"amount": <integer> // amount of tokens that the person designated by the address deposited
}, { ... }, ...
]
}, { ... }, ...
],
"donations": [ // rewards that the collective can control (e.g. transfer)
{
"denom": "<token-xyz>", // name of the token (denom)
"amount": <integer> // amount
}, { ... }, ...
],
"rewards": [ // rewards waiting to be distributed to the spending pool once the claim-period elapses
{
"denom": "<token-xyz>", // name of the token (denom)
"amount": <integer> // amount
}, { ... }, ...
]
},
"<collective-name-2>": { ... }, ...
NOTE: The claim of rewards and distribution of tokens does NOT have to occur precisely at the time of last-distro + claim-period, an optimization strategy should be chosen in which it’s most efficient to collect and re-distribute rewards to the spending pools. For example, collecting rewards can be done for all collectives simultaneously and distribution to spending pools can be done at a later block/time in accordance with individual spending pools claim-period’s.
III. Collective Management
Staking Collectives MUST be associated with at least ONE or many owners - governance-defined roles or individual KIRA accounts controlling the distribution to the specific spending pools. Collectives should be controlled using custom collective proposals that can be raised & voted on only by the collective owners. Any KIRA account should be able to create a collective and seed it with derivatives, thus becoming the first account in the list of owners.
There should be NO permissions necessary to vote on the collective proposal other than being part of the collective.
Owners of the Collective should be able to create and vote on the collective change proposals to:
Curate list of owners
Curate list of deposit-whitelist
Change status from active/inactive to paused, in the paused status only withdraws should be possible
Transfer any amount of donations as one or many tokens to selected address or addresses
Change claim conditions (start, period, end)
Modify description
Remove collective
IV. Collective Removal
The final consideration of the Collective management should be the removal of the collective from the registry, that is managing a situation in which owners decide to mutually delete it or the amount/value of staking derivatives deposited falls below the minimum as defined by min_collective_bond. Removal of the collective as a result of owner's proposal to initiate it MUST be possible regardless of whether or not some of the depositors’ derivate tokens in the Collective were locked by them. As a result of the successful Collective Removal proposal, all tokens deposited should be returned back to the depositors at the time of the proposal enactment while all other remaining tokens, such as donations and staking rewards should be claimed for a final time and sent to the spending pools.
In the case where depositors decide to remove their derivatives from the collective (assuming their coins are not in the locking state) and min_collective_bond would no longer be reached, the Collective should change its status to inactive and the automatic removal process must be initiated in the same manner as if removal proposal was raised and automatically passed. The main reason to raise a proposal is to the cleanup registry to prevent spam while retaining information for the owners in regard to the reason why the collective was removed. Unless depositors are also one of the owners they should NOT have the ability to raise or vote on any proposals, only in the case where they intend to remove their stake a Collective Removal proposal should be initiated.
NOTE: Withdrawal transaction should NOT be possible to be executed if it would result in the amount of staking derivative tokens amount falling below min_collective_bond. Instead, a Collective Removal proposal should be automatically raised.
V. Spending Pool Modification
Original Spending Pools were limited to a single token that can be distributed at a single rate. Since Staking Collectives require multiple tokens (staking rewards) to be distributed to the single spending pool we need to modify them to facilitate new functionality. Instead of a single token and a single rate we need to create an array of multiple tokens and corresponding distribution rates.
Dynamic Distribution Rates
Since the amount of staking rewards deposited from the Staking Collective to the Spending Pool changes unpredictably every claim-period (staking rewards vary from block to block) we need to create a mechanism of dynamic token rates to ensure that all staking rewards are spent within some specific time period.
A new boolean field dynamic-rate should be included in the spending pool structure to define whether or not dynamically adjusted token rates should be enforced. Once dynamic-rate is set to value “true” another field dynamic-rate-period should define every what period of time all the “token-rates” should change. If dynamic-rate is set to false, then the spending pool should operate in the same way as it would originally in KIP-71 (with the exception that it can support claiming multiple tokens and not just one).
In order to calculate new dynamic token rates we will utilize a "token-deposits” table in which all deposits will be collected for the time period equal to dynamic-rate-period. We will also need to calculate the total number of beneficiaries (users assigned by account and role) and divide their number by the total distribution rate we want to achieve. As the result, we would allow the fair distribution of all tokens deposited within dynamic-rate-period to a set of beneficiaries so that no tokens would be left at the end of the period. There is no need to take into account that the number of beneficiaries might be changing, the only thing that is important is the recalculation of token rates every dynamic-rate-period. To get the “total_beneficiaries_count” we will only take into account users in the claims table that registered their intent to withdraw tokens.
In essence, for each token x in token-deposits table, the new_token_rate(x) = ( token_deposits(x) / dynamic_rate_period ) / total_beneficiaries_count.
It might happen that the pool will run out of tokens because the number of beneficiaries suddenly increased. To mitigate that issue we should NOT allow for withdraws of tokens unless the beneficiary is eligible and registered to claim the tokens BEFORE the current dynamic-rate-period started. This means that new beneficiaries must await after registration for a new claim period before they can start receiving tokens from the pool. If dynamic-rate is set to true, then claim-expire should be ignored or set to value the same as dynamic-rate-period so that the maximum time period for which the user should be able to claim tokens is equal to dynamic-rate-period.
Distribution Weights
One of the new modifications to the Spending Pool should also be weights that define individual rates of token distribution. For example, if a person's weight is 2 they should receive tokens at 2x the token-rate, if the rate is 0.5, they should be able to claim the tokens but only at 1/2 the token-rate. By default, all token rates should be set to 1.
If any of the weights is changed to value different then 1, then token rates should be recalculated accordingly, that is for each token x in token-deposits table, the new_token_rate(x) = ( ( token_deposits(x) / dynamic_rate_period ) / total_beneficiaries_count ) * (weights_sum / total_beneficiaries_count)
Create a Spending Pool Registry with associated identifiers mapping to a list of properties defining how the pool operates:
JSON
Copy
<pool-name-1>: {
claim-start: <unix-timestamp>,
claim-end: <unix-timestamp>,
claim-expire: <seconds>,
vote-quorum: <percentage>, # default: 51%
vote-period: <seconds>, # default: 600s
vote-enactment: <seconds>, # default: 300s
"dynamic-rate": <boolean>, // (default false) defines if the rate of token distribution should be dynamic
"dynamic-rate-period": <integer>, // time in seconds defining every what period dynamic rates are calculated
"token-deposits": [ // list of tokens deposited to the spending pool but not yet distributed
{
"denom": "<string-name>",
"amount": <float-amount>
}, { ... }, ...
],
"token-rates": [ // list of tokens and their corresponding rates of distribution
{
"denom": "<string-name>",
"rate": <float-amount>
}, { ... }, ...
]
owners: {
roles: [ <role-name>, ... ]
accounts: [ <account-addr>, ... ]
},
beneficiaries: {
roles: [ {
"name": <role-name>,
"weight": <float-weight>
}, { ... }, ... ],
"accounts": [ {
"address": <account-addr>,
"weight": <float-weight>
}, { ... }, ...
, ... ]
},
claims: [
{ account: <account-addr>, date: <unix-timestamp>}, { ... }, ...
]
},
<pool-name-2>: { ... }, ...
Functions
staking-collective-create - allow ANY user to create Staking Collective even if they have no roles or permissions enabling that
spending-pool-create- a function to allow creating a new Spending Pool. This function MUST be possible to be executed by anyone, even if they have no roles or permissions.
staking-collective-contribute - can be sent by any whitelisted “contributor” account that wants to add tokens to the Staking Collective during or after creation process
staking-collective-withdraw - can be sent by any whitelisted “contributor” to withdraw their tokens (unless locking is enabled)
staking-collective-donate - allows to lock staking derivatives for a specific time period and donating a defined percentage of staking rewards to the collective.
proposal-staking-collective-send-donation - proposal to transfer accumulated donations to a specific account
proposal-staking-collective-update - proposal to update staking collective, e.g. change description, owners, contributors, spending-pool list, claim period, etc.
proposal-staking-collective-remove - proposal to remove/delete Staking Collective from registry
query-staking-collectives - query list of all staking collectives (output list of names), if name / id is specified then output full details of a single collective.
query-staking-collectives-proposals - list id of all proposals in regards to staking collectives, (or proposals in regards to a specific collective if name / id is specified in the query)
query-staking-collectives-by-account - query list of staking collectives by an individual KIRA address
Test Cases
Do not allow the creation of collectives with a name that already exists
Ensure that the collective creator must bond a minimum of 10% min_collective_bond while sending create tx and that there is no minimum deposit limit for any other contributors to the collective.
Ensure that collective is removed and NEVER becomes active if full min_collective_bond is NOT reached
Ensure that the collective creator and all depositors can retrieve their funds if the collective is removed or otherwise get them back automatically if such a dissolution event takes place during or after the creation of the collective.
Test withdraws in combination with locking period, verify if locking period can be extended (e.g. to 10y by sending locking tx 10 times)
Ensure that ANY staking derivative tokens as well as multiple staking derivative tokens can be used simultaneously to create collective but NOT any other types of tokens.
Ensure rewards start being claimed at claim-start and end at claim-end
Ensure rewards claim doesn’t occur more often than as defined by claim-period
Verify that every time rewards are distributed to the spending pool the last-claim property is updated
Verify that owners can raise and vote on proposals
Ensure that contributor can withdraw their tokens and trigger the collective removal proposal automatically if the token balance falls below min_collective_bond.
Ensure that collective status changes to inactive if the deposited balance falls below min_collective_bond. (e.g. one of the staking tokens is delisted or its value in regards to KEX changes)
Ensure that owners can transfer tokens from staking reward donations to any address of their choosing.
Verify that the spending pool module can distribute all deposited tokens to all registered beneficiaries, within dynamic-rate-period.
Create integration tests where Staking Collective and Spending Pool are interconnected and operational together.