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.