# Vesting
- Vesting
# Intro and Requirements
This specification defines the vesting account implementation that is used by
the Cosmos Hub. The requirements for this vesting account is that it should be
initialized during genesis with a starting balance X
and a vesting end
time ET
. A vesting account may be initialized with a vesting start time ST
and a number of vesting periods P
. If a vesting start time is included, the
vesting period does not begin until start time is reached. If vesting periods
are included, the vesting occurs over the specified number of periods.
For all vesting accounts, the owner of the vesting account is able to delegate and undelegate from validators, however they cannot transfer coins to another account until those coins are vested. This specification allows for four different kinds of vesting:
- Delayed vesting, where all coins are vested once
ET
is reached. - Continous vesting, where coins begin to vest at
ST
and vest linearly with respect to time untilET
is reached - Periodic vesting, where coins begin to vest at
ST
and vest periodically according to number of periods and the vesting amount per period. The number of periods, length per period, and amount per period are configurable. A periodic vesting account is distinguished from a continuous vesting account in that coins can be released in staggered tranches. For example, a periodic vesting account could be used for vesting arrangements where coins are relased quarterly, yearly, or over any other function of tokens over time. - Permanent locked vesting, where coins are locked forever. Coins in this account can still be used for delegating and for governance votes even while locked.
# Note
Vesting accounts can be initialized with some vesting and non-vesting coins.
The non-vesting coins would be immediately transferable. DelayedVesting and
ContinuousVesting accounts can be created with normal messages after genesis.
Other types of vesting accounts must be created at genesis, or as
part of a manual network upgrade. The current specification only allows
for unconditional vesting (ie. there is no possibility of reaching ET
and
having coins fail to vest).
# Vesting Account Types
# BaseVestingAccount
# ContinuousVestingAccount
# DelayedVestingAccount
# Period
# PeriodicVestingAccount
In order to facilitate less ad-hoc type checking and assertions and to support
flexibility in account balance usage, the existing x/bank
ViewKeeper
interface
is updated to contain the following:
# PermanentLockedAccount
# Vesting Account Specification
Given a vesting account, we define the following in the proceeding operations:
OV
: The original vesting coin amount. It is a constant value.V
: The number ofOV
coins that are still vesting. It is derived byOV
,StartTime
andEndTime
. This value is computed on demand and not on a per-block basis.V'
: The number ofOV
coins that are vested (unlocked). This value is computed on demand and not a per-block basis.DV
: The number of delegated vesting coins. It is a variable value. It is stored and modified directly in the vesting account.DF
: The number of delegated vested (unlocked) coins. It is a variable value. It is stored and modified directly in the vesting account.BC
: The number ofOV
coins less any coins that are transferred (which can be negative or delegated). It is considered to be balance of the embedded base account. It is stored and modified directly in the vesting account.
# Determining Vesting & Vested Amounts
It is important to note that these values are computed on demand and not on a
mandatory per-block basis (e.g. BeginBlocker
or EndBlocker
).
# Continuously Vesting Accounts
To determine the amount of coins that are vested for a given block time T
, the
following is performed:
- Compute
X := T - StartTime
- Compute
Y := EndTime - StartTime
- Compute
V' := OV * (X / Y)
- Compute
V := OV - V'
Thus, the total amount of vested coins is V'
and the remaining amount, V
,
is vesting.
# Periodic Vesting Accounts
Periodic vesting accounts require calculating the coins released during each
period for a given block time T
. Note that multiple periods could have passed
when calling GetVestedCoins
, so we must iterate over each period until the
end of that period is after T
.
- Set
CT := StartTime
- Set
V' := 0
For each Period P:
- Compute
X := T - CT
- IF
X >= P.Length
- Compute
V' += P.Amount
- Compute
CT += P.Length
- ELSE break
- Compute
- Compute
V := OV - V'
# Delayed/Discrete Vesting Accounts
Delayed vesting accounts are easier to reason about as they only have the full amount vesting up until a certain time, then all the coins become vested (unlocked). This does not include any unlocked coins the account may have initially.
# Transferring/Sending
At any given time, a vesting account may transfer: min((BC + DV) - V, BC)
.
In other words, a vesting account may transfer the minimum of the base account balance and the base account balance plus the number of currently delegated vesting coins less the number of coins vested so far.
However, given that account balances are tracked via the x/bank
module and that
we want to avoid loading the entire account balance, we can instead determine
the locked balance, which can be defined as max(V - DV, 0)
, and infer the
spendable balance from that.
The x/bank
ViewKeeper
can then provide APIs to determine locked and spendable
coins for any account:
# Keepers/Handlers
The corresponding x/bank
keeper should appropriately handle sending coins
based on if the account is a vesting account or not.
# Delegating
For a vesting account attempting to delegate D
coins, the following is performed:
- Verify
BC >= D > 0
- Compute
X := min(max(V - DV, 0), D)
(portion ofD
that is vesting) - Compute
Y := D - X
(portion ofD
that is free) - Set
DV += X
- Set
DF += Y
Note TrackDelegation
only modifies the DelegatedVesting
and DelegatedFree
fields, so upstream callers MUST modify the Coins
field by subtracting amount
.
# Keepers/Handlers
# Undelegating
For a vesting account attempting to undelegate D
coins, the following is performed:
NOTE: DV < D
and (DV + DF) < D
may be possible due to quirks in the rounding of
delegation/undelegation logic.
- Verify
D > 0
- Compute
X := min(DF, D)
(portion ofD
that should become free, prioritizing free coins) - Compute
Y := min(DV, D - X)
(portion ofD
that should remain vesting) - Set
DF -= X
- Set
DV -= Y
Note TrackUnDelegation
only modifies the DelegatedVesting
and DelegatedFree
fields, so upstream callers MUST modify the Coins
field by adding amount
.
Note: If a delegation is slashed, the continuous vesting account ends up
with an excess DV
amount, even after all its coins have vested. This is because
undelegating free coins are prioritized.
Note: The undelegation (bond refund) amount may exceed the delegated vesting (bond) amount due to the way undelegation truncates the bond refund, which can increase the validator's exchange rate (tokens/shares) slightly if the undelegated tokens are non-integral.
# Keepers/Handlers
# Keepers & Handlers
The VestingAccount
implementations reside in x/auth
. However, any keeper in
a module (e.g. staking in x/staking
) wishing to potentially utilize any vesting
coins, must call explicit methods on the x/bank
keeper (e.g. DelegateCoins
)
opposed to SendCoins
and SubtractCoins
.
In addition, the vesting account should also be able to spend any coins it
receives from other users. Thus, the bank module's MsgSend
handler should
error if a vesting account is trying to send an amount that exceeds their
unlocked coin amount.
See the above specification for full implementation details.
# Genesis Initialization
To initialize both vesting and non-vesting accounts, the GenesisAccount
struct
includes new fields: Vesting
, StartTime
, and EndTime
. Accounts meant to be
of type BaseAccount
or any non-vesting type have Vesting = false
. The
genesis initialization logic (e.g. initFromGenesisState
) must parse
and return the correct accounts accordingly based off of these fields.
# Examples
# Simple
Given a continuous vesting account with 10 vesting coins.
Immediately receives 1 coin
Time passes, 2 coins vest
Delegates 4 coins to validator A
Sends 3 coins
More time passes, 2 more coins vest
Sends 2 coins. At this point the account cannot send anymore until further coins vest or it receives additional coins. It can still however, delegate.
# Slashing
Same initial starting conditions as the simple example.
Time passes, 5 coins vest
Delegate 5 coins to validator A
Delegate 5 coins to validator B
Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins
Undelegate from validator A (2.5 coins)
Undelegate from validator B (5 coins). The account at this point can only send 2.5 coins unless it receives more coins or until more coins vest. It can still however, delegate.
Notice how we have an excess amount of
DV
.
# Periodic Vesting
A vesting account is created where 100 tokens will be released over 1 year, with 1/4 of tokens vesting each quarter. The vesting schedule would be as follows:
Immediately receives 1 coin
Vesting period 1 passes, 25 coins vest
During vesting period 2, 5 coins are transfered and 5 coins are delegated
Vesting period 2 passes, 25 coins vest
# Glossary
- OriginalVesting: The amount of coins (per denomination) that are initially part of a vesting account. These coins are set at genesis.
- StartTime: The BFT time at which a vesting account starts to vest.
- EndTime: The BFT time at which a vesting account is fully vested.
- DelegatedFree: The tracked amount of coins (per denomination) that are delegated from a vesting account that have been fully vested at time of delegation.
- DelegatedVesting: The tracked amount of coins (per denomination) that are delegated from a vesting account that were vesting at time of delegation.
- ContinuousVestingAccount: A vesting account implementation that vests coins linearly over time.
- DelayedVestingAccount: A vesting account implementation that only fully vests all coins at a given time.
- PeriodicVestingAccount: A vesting account implementation that vests coins according to a custom vesting schedule.
- PermanentLockedAccount: It does not ever release coins, locking them indefinitely. Coins in this account can still be used for delegating and for governance votes even while locked.