github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/vesting/README.md (about)

     1  ---
     2  sidebar_position: 1
     3  ---
     4  
     5  # `x/auth/vesting`
     6  
     7  
     8  * [Intro and Requirements](#intro-and-requirements)
     9  * [Note](#note)
    10  * [Vesting Account Types](#vesting-account-types)
    11      * [BaseVestingAccount](#basevestingaccount)
    12      * [ContinuousVestingAccount](#continuousvestingaccount)
    13      * [DelayedVestingAccount](#delayedvestingaccount)
    14      * [Period](#period)
    15      * [PeriodicVestingAccount](#periodicvestingaccount)
    16      * [PermanentLockedAccount](#permanentlockedaccount)
    17  * [Vesting Account Specification](#vesting-account-specification)
    18      * [Determining Vesting & Vested Amounts](#determining-vesting--vested-amounts)
    19      * [Periodic Vesting Accounts](#periodic-vesting-accounts)
    20      * [Transferring/Sending](#transferringsending)
    21      * [Delegating](#delegating)
    22      * [Undelegating](#undelegating)
    23  * [Keepers & Handlers](#keepers--handlers)
    24  * [Genesis Initialization](#genesis-initialization)
    25  * [Examples](#examples)
    26      * [Simple](#simple)
    27      * [Slashing](#slashing)
    28      * [Periodic Vesting](#periodic-vesting)
    29  * [Glossary](#glossary)
    30  
    31  ## Intro and Requirements
    32  
    33  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.
    34  
    35  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:
    36  
    37  * Delayed vesting, where all coins are vested once `ET` is reached.
    38  * Continous vesting, where coins begin to vest at `ST` and vest linearly with respect to time until `ET` is reached
    39  * 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.
    40  * 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.
    41  
    42  ## Note
    43  
    44  Vesting accounts can be initialized with some vesting and non-vesting coins. The non-vesting coins would be immediately transferable. DelayedVesting ContinuousVesting, PeriodicVesting and PermenantVesting 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
    45  having coins fail to vest).
    46  
    47  ## Vesting Account Types
    48  
    49  ```go
    50  // VestingAccount defines an interface that any vesting account type must
    51  // implement.
    52  type VestingAccount interface {
    53    Account
    54  
    55    GetVestedCoins(Time)  Coins
    56    GetVestingCoins(Time) Coins
    57  
    58    // TrackDelegation performs internal vesting accounting necessary when
    59    // delegating from a vesting account. It accepts the current block time, the
    60    // delegation amount and balance of all coins whose denomination exists in
    61    // the account's original vesting balance.
    62    TrackDelegation(Time, Coins, Coins)
    63  
    64    // TrackUndelegation performs internal vesting accounting necessary when a
    65    // vesting account performs an undelegation.
    66    TrackUndelegation(Coins)
    67  
    68    GetStartTime() int64
    69    GetEndTime()   int64
    70  }
    71  ```
    72  
    73  ### BaseVestingAccount
    74  
    75  ```protobuf reference
    76  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L11-L35
    77  ```
    78  
    79  ### ContinuousVestingAccount
    80  
    81  ```protobuf reference
    82  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L37-L46
    83  ```
    84  
    85  ### DelayedVestingAccount
    86  
    87  ```protobuf reference
    88  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L48-L57
    89  ```
    90  
    91  ### Period
    92  
    93  ```protobuf reference
    94  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L59-L69
    95  ```
    96  
    97  ```go
    98  // Stores all vesting periods passed as part of a PeriodicVestingAccount
    99  type Periods []Period
   100  
   101  ```
   102  
   103  ### PeriodicVestingAccount
   104  
   105  ```protobuf reference
   106  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L71-L81
   107  ```
   108  
   109  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:
   110  
   111  ```go
   112  type ViewKeeper interface {
   113    // ...
   114  
   115    // Calculates the total locked account balance.
   116    LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
   117  
   118    // Calculates the total spendable balance that can be sent to other accounts.
   119    SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
   120  }
   121  ```
   122  
   123  ### PermanentLockedAccount
   124  
   125  ```protobuf reference
   126  https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/vesting/v1beta1/vesting.proto#L83-L94
   127  ```
   128  
   129  ## Vesting Account Specification
   130  
   131  Given a vesting account, we define the following in the proceeding operations:
   132  
   133  * `OV`: The original vesting coin amount. It is a constant value.
   134  * `V`: The number of `OV` coins that are still _vesting_. It is derived by
   135  `OV`, `StartTime` and `EndTime`. This value is computed on demand and not on a per-block basis.
   136  * `V'`: The number of `OV` coins that are _vested_ (unlocked). This value is computed on demand and not a per-block basis.
   137  * `DV`: The number of delegated _vesting_ coins. It is a variable value. It is stored and modified directly in the vesting account.
   138  * `DF`: The number of delegated _vested_ (unlocked) coins. It is a variable value. It is stored and modified directly in the vesting account.
   139  * `BC`: The number of `OV` coins less any coins that are transferred
   140  (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.
   141  
   142  ### Determining Vesting & Vested Amounts
   143  
   144  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`).
   145  
   146  #### Continuously Vesting Accounts
   147  
   148  To determine the amount of coins that are vested for a given block time `T`, the
   149  following is performed:
   150  
   151  1. Compute `X := T - StartTime`
   152  2. Compute `Y := EndTime - StartTime`
   153  3. Compute `V' := OV * (X / Y)`
   154  4. Compute `V := OV - V'`
   155  
   156  Thus, the total amount of _vested_ coins is `V'` and the remaining amount, `V`,
   157  is _vesting_.
   158  
   159  ```go
   160  func (cva ContinuousVestingAccount) GetVestedCoins(t Time) Coins {
   161      if t <= cva.StartTime {
   162          // We must handle the case where the start time for a vesting account has
   163          // been set into the future or when the start of the chain is not exactly
   164          // known.
   165          return ZeroCoins
   166      } else if t >= cva.EndTime {
   167          return cva.OriginalVesting
   168      }
   169  
   170      x := t - cva.StartTime
   171      y := cva.EndTime - cva.StartTime
   172  
   173      return cva.OriginalVesting * (x / y)
   174  }
   175  
   176  func (cva ContinuousVestingAccount) GetVestingCoins(t Time) Coins {
   177      return cva.OriginalVesting - cva.GetVestedCoins(t)
   178  }
   179  ```
   180  
   181  ### Periodic Vesting Accounts
   182  
   183  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`.
   184  
   185  1. Set `CT := StartTime`
   186  2. Set `V' := 0`
   187  
   188  For each Period P:
   189  
   190    1. Compute `X := T - CT`
   191    2. IF `X >= P.Length`
   192        1. Compute `V' += P.Amount`
   193        2. Compute `CT += P.Length`
   194        3. ELSE break
   195    3. Compute `V := OV - V'`
   196  
   197  ```go
   198  func (pva PeriodicVestingAccount) GetVestedCoins(t Time) Coins {
   199    if t < pva.StartTime {
   200      return ZeroCoins
   201    }
   202    ct := pva.StartTime // The start of the vesting schedule
   203    vested := 0
   204    periods = pva.GetPeriods()
   205    for _, period  := range periods {
   206      if t - ct < period.Length {
   207        break
   208      }
   209      vested += period.Amount
   210      ct += period.Length // increment ct to the start of the next vesting period
   211    }
   212    return vested
   213  }
   214  
   215  func (pva PeriodicVestingAccount) GetVestingCoins(t Time) Coins {
   216      return pva.OriginalVesting - cva.GetVestedCoins(t)
   217  }
   218  ```
   219  
   220  #### Delayed/Discrete Vesting Accounts
   221  
   222  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.
   223  
   224  ```go
   225  func (dva DelayedVestingAccount) GetVestedCoins(t Time) Coins {
   226      if t >= dva.EndTime {
   227          return dva.OriginalVesting
   228      }
   229  
   230      return ZeroCoins
   231  }
   232  
   233  func (dva DelayedVestingAccount) GetVestingCoins(t Time) Coins {
   234      return dva.OriginalVesting - dva.GetVestedCoins(t)
   235  }
   236  ```
   237  
   238  ### Transferring/Sending
   239  
   240  At any given time, a vesting account may transfer: `min((BC + DV) - V, BC)`.
   241  
   242  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.
   243  
   244  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.
   245  
   246  ```go
   247  func (va VestingAccount) LockedCoins(t Time) Coins {
   248     return max(va.GetVestingCoins(t) - va.DelegatedVesting, 0)
   249  }
   250  ```
   251  
   252  The `x/bank` `ViewKeeper` can then provide APIs to determine locked and spendable coins for any account:
   253  
   254  ```go
   255  func (k Keeper) LockedCoins(ctx Context, addr AccAddress) Coins {
   256      acc := k.GetAccount(ctx, addr)
   257      if acc != nil {
   258          if acc.IsVesting() {
   259              return acc.LockedCoins(ctx.BlockTime())
   260          }
   261      }
   262  
   263      // non-vesting accounts do not have any locked coins
   264      return NewCoins()
   265  }
   266  ```
   267  
   268  #### Keepers/Handlers
   269  
   270  The corresponding `x/bank` keeper should appropriately handle sending coins based on if the account is a vesting account or not.
   271  
   272  ```go
   273  func (k Keeper) SendCoins(ctx Context, from Account, to Account, amount Coins) {
   274      bc := k.GetBalances(ctx, from)
   275      v := k.LockedCoins(ctx, from)
   276  
   277      spendable := bc - v
   278      newCoins := spendable - amount
   279      assert(newCoins >= 0)
   280  
   281      from.SetBalance(newCoins)
   282      to.AddBalance(amount)
   283  
   284      // save balances...
   285  }
   286  ```
   287  
   288  ### Delegating
   289  
   290  For a vesting account attempting to delegate `D` coins, the following is performed:
   291  
   292  1. Verify `BC >= D > 0`
   293  2. Compute `X := min(max(V - DV, 0), D)` (portion of `D` that is vesting)
   294  3. Compute `Y := D - X` (portion of `D` that is free)
   295  4. Set `DV += X`
   296  5. Set `DF += Y`
   297  
   298  ```go
   299  func (va VestingAccount) TrackDelegation(t Time, balance Coins, amount Coins) {
   300      assert(balance <= amount)
   301      x := min(max(va.GetVestingCoins(t) - va.DelegatedVesting, 0), amount)
   302      y := amount - x
   303  
   304      va.DelegatedVesting += x
   305      va.DelegatedFree += y
   306  }
   307  ```
   308  
   309  **Note** `TrackDelegation` only modifies the `DelegatedVesting` and `DelegatedFree` fields, so upstream callers MUST modify the `Coins` field by subtracting `amount`.
   310  
   311  #### Keepers/Handlers
   312  
   313  ```go
   314  func DelegateCoins(t Time, from Account, amount Coins) {
   315      if isVesting(from) {
   316          from.TrackDelegation(t, amount)
   317      } else {
   318          from.SetBalance(sc - amount)
   319      }
   320  
   321      // save account...
   322  }
   323  ```
   324  
   325  ### Undelegating
   326  
   327  For a vesting account attempting to undelegate `D` coins, the following is performed:
   328  
   329  > NOTE: `DV < D` and `(DV + DF) < D` may be possible due to quirks in the rounding of delegation/undelegation logic.
   330  
   331  1. Verify `D > 0`
   332  2. Compute `X := min(DF, D)` (portion of `D` that should become free, prioritizing free coins)
   333  3. Compute `Y := min(DV, D - X)` (portion of `D` that should remain vesting)
   334  4. Set `DF -= X`
   335  5. Set `DV -= Y`
   336  
   337  ```go
   338  func (cva ContinuousVestingAccount) TrackUndelegation(amount Coins) {
   339      x := min(cva.DelegatedFree, amount)
   340      y := amount - x
   341  
   342      cva.DelegatedFree -= x
   343      cva.DelegatedVesting -= y
   344  }
   345  ```
   346  
   347  **Note** `TrackUnDelegation` only modifies the `DelegatedVesting` and `DelegatedFree` fields, so upstream callers MUST modify the `Coins` field by adding `amount`.
   348  
   349  **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.
   350  
   351  **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.
   352  
   353  #### Keepers/Handlers
   354  
   355  ```go
   356  func UndelegateCoins(to Account, amount Coins) {
   357      if isVesting(to) {
   358          if to.DelegatedFree + to.DelegatedVesting >= amount {
   359              to.TrackUndelegation(amount)
   360              // save account ...
   361          }
   362      } else {
   363          AddBalance(to, amount)
   364          // save account...
   365      }
   366  }
   367  ```
   368  
   369  ## Keepers & Handlers
   370  
   371  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`.
   372  
   373  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.
   374  
   375  See the above specification for full implementation details.
   376  
   377  ## Genesis Initialization
   378  
   379  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.
   380  
   381  ```go
   382  type GenesisAccount struct {
   383      // ...
   384  
   385      // vesting account fields
   386      OriginalVesting  sdk.Coins `json:"original_vesting"`
   387      DelegatedFree    sdk.Coins `json:"delegated_free"`
   388      DelegatedVesting sdk.Coins `json:"delegated_vesting"`
   389      StartTime        int64     `json:"start_time"`
   390      EndTime          int64     `json:"end_time"`
   391  }
   392  
   393  func ToAccount(gacc GenesisAccount) Account {
   394      bacc := NewBaseAccount(gacc)
   395  
   396      if gacc.OriginalVesting > 0 {
   397          if ga.StartTime != 0 && ga.EndTime != 0 {
   398              // return a continuous vesting account
   399          } else if ga.EndTime != 0 {
   400              // return a delayed vesting account
   401          } else {
   402              // invalid genesis vesting account provided
   403              panic()
   404          }
   405      }
   406  
   407      return bacc
   408  }
   409  ```
   410  
   411  ## Examples
   412  
   413  ### Simple
   414  
   415  Given a continuous vesting account with 10 vesting coins.
   416  
   417  ```text
   418  OV = 10
   419  DF = 0
   420  DV = 0
   421  BC = 10
   422  V = 10
   423  V' = 0
   424  ```
   425  
   426  1. Immediately receives 1 coin
   427  
   428      ```text
   429      BC = 11
   430      ```
   431  
   432  2. Time passes, 2 coins vest
   433  
   434      ```text
   435      V = 8
   436      V' = 2
   437      ```
   438  
   439  3. Delegates 4 coins to validator A
   440  
   441      ```text
   442      DV = 4
   443      BC = 7
   444      ```
   445  
   446  4. Sends 3 coins
   447  
   448      ```text
   449      BC = 4
   450      ```
   451  
   452  5. More time passes, 2 more coins vest
   453  
   454      ```text
   455      V = 6
   456      V' = 4
   457      ```
   458  
   459  6. Sends 2 coins. At this point the account cannot send anymore until further
   460  coins vest or it receives additional coins. It can still however, delegate.
   461  
   462      ```text
   463      BC = 2
   464      ```
   465  
   466  ### Slashing
   467  
   468  Same initial starting conditions as the simple example.
   469  
   470  1. Time passes, 5 coins vest
   471  
   472      ```text
   473      V = 5
   474      V' = 5
   475      ```
   476  
   477  2. Delegate 5 coins to validator A
   478  
   479      ```text
   480      DV = 5
   481      BC = 5
   482      ```
   483  
   484  3. Delegate 5 coins to validator B
   485  
   486      ```text
   487      DF = 5
   488      BC = 0
   489      ```
   490  
   491  4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins
   492  5. Undelegate from validator A (2.5 coins)
   493  
   494      ```text
   495      DF = 5 - 2.5 = 2.5
   496      BC = 0 + 2.5 = 2.5
   497      ```
   498  
   499  6. Undelegate from validator B (5 coins). The account at this point can only
   500  send 2.5 coins unless it receives more coins or until more coins vest.
   501  It can still however, delegate.
   502  
   503      ```text
   504      DV = 5 - 2.5 = 2.5
   505      DF = 2.5 - 2.5 = 0
   506      BC = 2.5 + 5 = 7.5
   507      ```
   508  
   509      Notice how we have an excess amount of `DV`.
   510  
   511  ### Periodic Vesting
   512  
   513  A vesting account is created where 100 tokens will be released over 1 year, with
   514  1/4 of tokens vesting each quarter. The vesting schedule would be as follows:
   515  
   516  ```yaml
   517  Periods:
   518  - amount: 25stake, length: 7884000
   519  - amount: 25stake, length: 7884000
   520  - amount: 25stake, length: 7884000
   521  - amount: 25stake, length: 7884000
   522  ```
   523  
   524  ```text
   525  OV = 100
   526  DF = 0
   527  DV = 0
   528  BC = 100
   529  V = 100
   530  V' = 0
   531  ```
   532  
   533  1. Immediately receives 1 coin
   534  
   535      ```text
   536      BC = 101
   537      ```
   538  
   539  2. Vesting period 1 passes, 25 coins vest
   540  
   541      ```text
   542      V = 75
   543      V' = 25
   544      ```
   545  
   546  3. During vesting period 2, 5 coins are transfered and 5 coins are delegated
   547  
   548      ```text
   549      DV = 5
   550      BC = 91
   551      ```
   552  
   553  4. Vesting period 2 passes, 25 coins vest
   554  
   555      ```text
   556      V = 50
   557      V' = 50
   558      ```
   559  
   560  ## Glossary
   561  
   562  * OriginalVesting: The amount of coins (per denomination) that are initially
   563  part of a vesting account. These coins are set at genesis.
   564  * StartTime: The BFT time at which a vesting account starts to vest.
   565  * EndTime: The BFT time at which a vesting account is fully vested.
   566  * DelegatedFree: The tracked amount of coins (per denomination) that are
   567  delegated from a vesting account that have been fully vested at time of delegation.
   568  * DelegatedVesting: The tracked amount of coins (per denomination) that are
   569  delegated from a vesting account that were vesting at time of delegation.
   570  * ContinuousVestingAccount: A vesting account implementation that vests coins
   571  linearly over time.
   572  * DelayedVestingAccount: A vesting account implementation that only fully vests
   573  all coins at a given time.
   574  * PeriodicVestingAccount: A vesting account implementation that vests coins
   575  according to a custom vesting schedule.
   576  * PermanentLockedAccount: It does not ever release coins, locking them indefinitely.
   577  Coins in this account can still be used for delegating and for governance votes even while locked.
   578  
   579  
   580  ## CLI
   581  
   582  A user can query and interact with the `vesting` module using the CLI.
   583  
   584  ### Transactions
   585  
   586  The `tx` commands allow users to interact with the `vesting` module.
   587  
   588  ```bash
   589  simd tx vesting --help
   590  ```
   591  
   592  #### create-periodic-vesting-account
   593  
   594  The `create-periodic-vesting-account` command creates a new vesting account funded with an allocation of tokens, where a sequence of coins and period length in seconds. Periods are sequential, in that the duration of of a period only starts at the end of the previous period. The duration of the first period starts upon account creation.
   595  
   596  ```bash
   597  simd tx vesting create-periodic-vesting-account [to_address] [periods_json_file] [flags]
   598  ```
   599  
   600  Example:
   601  
   602  ```bash
   603  simd tx vesting create-periodic-vesting-account cosmos1.. periods.json
   604  ```
   605  
   606  #### create-vesting-account
   607  
   608  The `create-vesting-account` command creates a new vesting account funded with an allocation of tokens. The account can either be a delayed or continuous vesting account, which is determined by the '--delayed' flag. All vesting accouts created will have their start time set by the committed block's time. The end_time must be provided as a UNIX epoch timestamp.
   609  
   610  ```bash
   611  simd tx vesting create-vesting-account [to_address] [amount] [end_time] [flags]
   612  ```
   613  
   614  Example:
   615  
   616  ```bash
   617  simd tx vesting create-vesting-account cosmos1.. 100stake 2592000
   618  ```