cosmos-sdk/x/accounts/defaults/lockup
2025-02-04 08:02:21 +00:00
..
depinject feat(accounts): make x/accounts more depinject friendly (#21928) 2024-09-30 07:51:50 +00:00
v1 fix: Add sims export/import test and fix (#23462) 2025-01-20 09:57:10 +00:00
CHANGELOG.md chore: sync changelogs (#22992) 2024-12-19 07:32:49 +00:00
continuous_locking_account_test.go fix(docs): various typos and misspelled function names (#23600) 2025-02-03 16:10:02 -05:00
continuous_locking_account.go fix(x/accounts/default/lockup): Lockup account track undelegation when unbonding entry is mature (#22254) 2024-12-17 16:22:15 +00:00
delayed_locking_account_test.go chore: remove simapp v1 (#23009) 2024-12-19 18:51:59 +00:00
delayed_locking_account.go fix(x/accounts/default/lockup): Lockup account track undelegation when unbonding entry is mature (#22254) 2024-12-17 16:22:15 +00:00
go.mod chore: bump comet for bug fix (#23601) 2025-02-04 08:02:21 +00:00
go.sum chore: bump comet for bug fix (#23601) 2025-02-04 08:02:21 +00:00
lockup_test.go fix(x/accounts/default/lockup): Lockup account track undelegation when unbonding entry is mature (#22254) 2024-12-17 16:22:15 +00:00
lockup.go chore: remove simapp v1 (#23009) 2024-12-19 18:51:59 +00:00
periodic_locking_account_test.go chore: remove simapp v1 (#23009) 2024-12-19 18:51:59 +00:00
periodic_locking_account.go fix(x/accounts/default/lockup): Lockup account track undelegation when unbonding entry is mature (#22254) 2024-12-17 16:22:15 +00:00
permanent_locking_account_test.go chore: remove simapp v1 (#23009) 2024-12-19 18:51:59 +00:00
permanent_locking_account.go fix(x/accounts/default/lockup): Lockup account track undelegation when unbonding entry is mature (#22254) 2024-12-17 16:22:15 +00:00
README.md docs: fix typos (#23152) 2025-01-02 15:02:09 +00:00
TUTORIAL.md chore: fix spelling errors (#22802) 2024-12-09 12:26:05 +00:00
utils_test.go chore: remove simapp v1 (#23009) 2024-12-19 18:51:59 +00:00
validate.go feat(x/accounts): Add new lockup account type (#19048) 2024-03-14 09:06:02 +00:00

Lockup Accounts

The x/accounts/defaults/lockup module provides the implementation for lockup accounts within the x/accounts module.

Lockup Account Types

BaseLockup

The base lockup account is used by all default lockup accounts. It contains the basic information for a lockup account. The Base lockup account keeps knowledge of the staking delegations from the account.

type BaseLockup struct {
	// Owner is the address of the account owner.
	Owner            collections.Item[[]byte]
	OriginalLocking  collections.Map[string, math.Int]
	DelegatedFree    collections.Map[string, math.Int]
	DelegatedLocking collections.Map[string, math.Int]
	WithdrawedCoins  collections.Map[string, math.Int]
	addressCodec     address.Codec
	headerService    header.Service
	// lockup end time.
	EndTime collections.Item[time.Time]
}

ContinuousLockup

The continuous lockup account has a future start time and begins unlocking continuously until the specified end date.

To determine the amount of coins that are vested for a given block time T, the following is performed:

  1. Compute X := T - StartTime
  2. Compute Y := EndTime - StartTime
  3. Compute V' := OV * (X / Y)
  4. Compute V := OV - V'

Thus, the total amount of vested coins is V' and the remaining amount, V, is lockup.

type ContinuousLockingAccount struct {
	*BaseLockup
	StartTime collections.Item[time.Time]
}

DelayedLockup

The delayed lockup account unlocks all tokens at a specific time. The account can receive coins and send coins. The account can be used to lock coins for a long period of time.

type DelayedLockingAccount struct {
	*BaseLockup
}

PeriodicLockup

The periodic lockup account locks tokens for a series of periods. The account can receive coins and send coins. After all the periods, all the coins are unlocked and the account can send coins.

Periodic lockup 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.

  1. Set CT := StartTime
  2. Set V' := 0

For each Period P:

  1. Compute X := T - CT
  2. IF X >= P.Length
    1. Compute V' += P.Amount
    2. Compute CT += P.Length
    3. ELSE break
  3. Compute V := OV - V'
type PeriodicLockingAccount struct {
	*BaseLockup
	StartTime      collections.Item[time.Time]
	LockingPeriods collections.Vec[lockuptypes.Period]
}

PermanentLocked

The permanent lockup account permanently locks the coins in the account. The account can only receive coins and cannot send coins. The account can be used to lock coins for a long period of time.

type PermanentLockingAccount struct {
	*BaseLockup
}

Genesis Initialization

In An Event Of Slashing

As defined, base lockup store DelegatedLocking by amount. In an event of a validator that the lockup account delegate to is slash which affect the actual delegation amount, this will leave the DelegatedLocking have an excess amount even if user undelegate all of the account delegated amount. This excess amount would affect the spendable amount, further details are as below:

The spendable amount is calculated as: spendableAmount = balance - notBondedLockedAmount where notBondedLockedAmount = lockedAmount - Min(lockedAmount, delegatedLockedAmount)

As seen in the formula notBondedLockedAmount can only be 0 or a positive value when DelegatedLockedAmount < LockedAmount. Let call NewDelegatedLockedAmount is the delegatedLockedAmount when applying N slash

  1. Case 1: Originally DelegatedLockedAmount > lockedAmount but when applying the slash amount the NewDelegatedLockedAmount < lockedAmount then
    • When not applying slash notBondedLockedAmount will be 0
    • When apply slash notBondedLockedAmount will be lockedAmount - NewDelegatedLockedAmount = a positive amount
  2. Case 2: where originally DelegatedLockedAmount < lockedAmount when applying the slash amount the NewDelegatedLockedAmount < lockedAmount then
    • When not applying slash lockedAmount - DelegatedLockedAmount
    • When apply slash notBondedLockedAmount will be lockedAmount - NewDelegatedLockedAmount = lockedAmount - (DelegatedLockedAmount - N) = lockedAmount - DelegatedLockedAmount + N
  3. Case 3: where originally DelegatedLockedAmount > lockedAmount when applying the slash amount still the NewDelegatedLockedAmount > lockedAmount then notBondedLockedAmount will be 0 applying slash or not

In cases 1 and 2, notBondedLockedAmount decreases when not applying the slash, resulting in a higher spendableAmount.

Due to the nature of x/accounts, as other modules cannot assume certain account types exist so the handling of slashing event must be done internally within x/accounts's accounts. For lockup accounts, this would make the logic overcomplicated. Since these effects are only an edge case that affect a small number of users, so here we would accept the trade off for a simpler design. This design decision aligns with the legacy vesting account implementation.

Examples

Simple

Given a continuous lockup account with 10 vested coins.

OV = 10
DF = 0
DV = 0
BC = 10
V = 10
V' = 0
  1. Immediately receives 1 coin

    BC = 11
    
  2. Time passes, 2 coins vest

    V = 8
    V' = 2
    
  3. Delegates 4 coins to validator A

    DV = 4
    BC = 7
    
  4. Sends 3 coins

    BC = 4
    
  5. More time passes, 2 more coins vest

    V = 6
    V' = 4
    
  6. Sends 2 coins. At this point, the account cannot send anymore until further coins vest or it receives additional coins. It can still, however, delegate.

    BC = 2
    

Slashing

Same initial starting conditions as the simple example.

  1. Time passes, 5 coins vest

    V = 5
    V' = 5
    
  2. Delegate 5 coins to validator A

    DV = 5
    BC = 5
    
  3. Delegate 5 coins to validator B

    DF = 5
    BC = 0
    
  4. Validator A gets slashed by 50%, making the delegation to A now worth 2.5 coins

  5. Undelegate from validator A (2.5 coins)

    DF = 5 - 2.5 = 2.5
    BC = 0 + 2.5 = 2.5
    
  6. Undelegate from validator B (5 coins). The account at this point can only send 2.5 coins unless it receives more coins or until more coins vest. It can still, however, delegate.

    DV = 5 - 2.5 = 2.5
    DF = 2.5 - 2.5 = 0
    BC = 2.5 + 5 = 7.5
    

    Notice how we have an excess amount of DV. This is explained in In An Event Of Slashing

Periodic Lockup

A lockup account is created where 100 tokens will be released over 1 year, with 1/4 of tokens vesting each quarter. The lockup schedule would be as follows:

Periods:
- amount: 25stake, length: 7884000
- amount: 25stake, length: 7884000
- amount: 25stake, length: 7884000
- amount: 25stake, length: 7884000
OV = 100
DF = 0
DV = 0
BC = 100
V = 100
V' = 0
  1. Immediately receives 1 coin

    BC = 101
    
  2. Lockup period 1 passes, 25 coins vest

    V = 75
    V' = 25
    
  3. During lockup period 2, 5 coins are transferred and 5 coins are delegated

    DV = 5
    BC = 91
    
  4. Lockup period 2 passes, 25 coins vest

    V = 50
    V' = 50