## Nomination Pools

---
## Problem
- Direct Nominators: Money is locked, and locked money can be used for governance.
- Pool nominators: Money is transferred from the nominator to the pooled account. The pooled account is the direct nominator.
---
## Staking
5 primary functions:
- Bond
- Unbond
- Withdraw
- Reward
- Slash
---
## How Pool nominators can participate in governance?
- Instead of transferring money to pooled account, we lock the money in place.
- The pool account is a pseudo account whose balance is represented as a sum of all pooled nominator's locked balances.
---
## Concerns
- Rewarding and slashing.
- Who does the lock? Is it nomination pool or staking pallet?
- If it is nomination pool who is responsible for locking, staking is trusting an outside pallet to make sure it has locked the money staking pallet is adding to its ledger.
---
### Staking Ledger
```rust=
pub struct StakingLedger<T: Config> {
/// The stash account whose balance is actually locked and at stake.
pub stash: T::AccountId,
/// The total amount thats locked (active + unlocking)
pub total: BalanceOf<T>,
/// Total amount that is eligible for reward (and slash) in forthcoming rounds
pub active: BalanceOf<T>,
/// Any balance that is becoming free
pub unlocking: Vec<UnlockChunk<BalanceOf<T>>>,
}
pub struct UnlockChunk<Balance: HasCompact + MaxEncodedLen> {
/// Amount of funds to be unlocked.
value: Balance,
/// Era number at which point it'll be unlocked.
era: EraIndex,
}
```
---
## Possible solution
- Staking allows a trusted primitive operation to allow an account (sub nominator) stake against another account (super nominator).
- Super nominator is a pseudo account. Staking manages all the lock and maintains a register of locked balance for the super account.
- Nomination pool uses the above function to build a pool of nominators.
---
## Staking responsibilities
- Ensure at any moment of time the active stake of a nominator is locked and cannot be moved.
- Let the higher order pallet (nomination pool) take care of reward and slashing.
- Slashing in this case would be lazy (compared to eager slashing in case of direct nominators).
---
## Nomination pools responsibilities
- Manage fair rewarding and slashing of pool members.
---
## Lazy slashing concerns
- Pending slashes can still be used for governance.
---
## Technical Details
Lets look at 5 primary operations and how they might work with the above approach.
- Bond
- Unbond
- Withdraw
- Reward
- Slash
---
### Staking::bond
- Update ledger of stash account.
- Lock amount.
----
### Staking::super_bond
- `fn super_bond(super, sub, value)`: New internal (trusted) api exposed by Staking pallet.
- In the normal bond, a nominator puts their fund at stake and makes a nomination.
- In `super_bond`, a sub nominator puts their fund at stake to a `super nominator` who can use this fund on behalf of the `sub`.
----
```rust=
fn super_bond(super: AccountId, sub: AccountId, value: Balance) {
// lock identifier should be different for sub nominators
// to direct nominators.
lock(lock_id, sub, value);
<SubNominators<T>>::insert(super, sub, value);
SuperNominatorLedger::<T>::mutate(|ledger| ledger.balance += value);
// The bond checks if its a super nominator that is trying to bond
// and looks up their balance from super ledger.
bond(super, value);
}
```
----
### New storage items
```rust=
pub type SubNominators<T: Config> = StorageDoubleMap<
_,
Twox64Concat,
T::AccountId, // super nominator
Twox64Concat,
T::AccountId, // child nominator
BalanceOf<T>, // locked balance
ValueQuery,
>;
pub type SuperNominatorLedger<T: Config> = CountedStorageMap<
_,
Twox64Concat,
T::AccountId, // who
SuperLedger<T>,
ValueQuery,
>;
pub struct SuperLedger<T: Config> {
// pub who: T::AccountId,
pub balance: BalanceOf<T>, // locked so we guarantee that money exists.
pub pending_slash: BalanceOf<T>,
// pub distribution: Vec<(T::AccountId, BalanceOf<T>)>,
}
impl SuperLedger<T> {
pub fn is_frozen(&self) -> bool { // probably not needed here
self.pending_slash > Zero::zero()
}
pub fn balance(&self) -> BalanceOf<T> {
self.balance - self.pending_slash
}
}
```
---
### Staking::unbond
- No unlock happens. New unlock chunks keyed by unlocking era are created.
----
### Staking::super_unbond
- Since no unlock happens, nothing changes here.
---
### Staking::withdraw
- Tries to consolidate unlock chunks available to withdraw.
- Unlocks withdrawable amount and updates `StakingLedger`.
----
### Staking::super_withdraw
- Pretty similar to normal withdraw except staking needs to know which sub to unlock.
- Can only be unlocked one sub at a time to a maximum value of the locked balance of the sub.
- Updates `SubNominators` and `SuperLedger`.
---
### Rewards
- Rewards are paid to an account passed by super nominator and its the responsibility of nomination pool to manage the rewards.
---
### Slashing
- `StakingLedger` is slashed.
- Locked balance is slashed.
----
### Staking::super_slash
- `StakingLedger` is slashed as usual.
- In the super nominator approach, we could slash all sub nominators in proportion but this will be very expensive. Instead we implement a lazy slashing solution.
- Super Ledger is updated with pending slash balance.
- No unbond possible if `pending_slash > balance`.
----
## Responsibilities of Nomination Pools
- No bond extra should be possible from pool account till pending slashes are applied. This is because new bond would be affected by pending slash if we do not apply slash before they join.
----
- Unbond is possible as we can calculate the effective balance of the pool (`balance - pending_slash`). We know the total points issued to the pool and can determine the unbonder's share. This operation will also partially apply a pending slash.
----
- `fn super_slash(super, sub, value)` will apply slash one sub at a time. Fails if `pending slash` is zero. Once pending_slashes are applied, account is unfrozen for `bond_extra`.
- Applying `pending_slashes` can be permissionless and the caller could also potentially be rewarded.
---
WIP...
---
## Nomination Pool implementation
- When a user joins a pool, they are issued points in the ratio points/balance.
- Points can only be greater than or equal to balance.
- It can only be greater if there is a slash.
- The locked balance cannot be used as points.
---
### Join
- Pool has total points issued: 500 and balance: 400.
- Joining a pool with 100 DOTS * 500 (total points) / 400 (pool balance) = 125 points are issued.
---
### Slash while Withdraw
- The pool now has points issued: 700 and balance = 560, i.e. no further slashing.
- User should get 125*560/700 balance = 100 DOTs back.
- In event of slash => total points = 700, balance = 500, user should get 89.3 DOTs back.
- 10.7 DOT should be applied as slash.
---
### Permissionless Slash
- In above case, 60 DOTs is pending as slash (560 - 500).
- Per point: 60/700
- Slash for user with 120 points: 125 * 60/700 = 10.71.
- Withdrawal is fine but no new bond till slashes are applied.
---
### Slash approaches
- Idea 1: All slashes need to be performed in one block to make sure a pool member does not get double slashed.
- Idea 2: On idle / On init processing of slashes in multiple pages. Till then a pool is blocked.
{"metaMigratedAt":"2023-06-18T04:12:07.484Z","metaMigratedFrom":"YAML","title":"Governance with pool funds","breaks":true,"slideOptions":"{\"transition\":\"fade\"}","contributors":"[{\"id\":\"33174ac8-6465-4419-a9c8-69548e822262\",\"add\":9505,\"del\":1814}]","description":"nomination pools"}