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 ```