github.com/Finschia/finschia-sdk@v0.49.1/x/auth/spec/05_vesting.md (about)

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