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