Escrow
Overview
The Escrow module allows users to lockup tokens in exchange for a non-fungible voting asset. The total “power” of this asset decays linearly as the lock approaches expiry - calculated based on the block height. Historic points for the linear function are recorded each time a user’s balance is adjusted which allows us to re-construct voting power at a particular point in time.
This architecture was adopted from Curve, see: Vote-Escrowed CRV (veCRV).
Note
This specification is still a Work-in-Progress (WIP), some information may be outdated or incomplete.
Step-by-step
A user may lock any amount of defined governance currency (KINT on Kintsugi, INTR on Interlay) up to a maximum lock period.
Both the amount and the unlock time may be increased to improve voting power.
The user may unlock their fungible asset after the lock has expired.
Data Model
Constants
Span
The locktime is rounded to weeks to limit checkpoint iteration.
MaxPeriod
The maximum period for lockup.
Scalars
Epoch
The current global epoch for PointHistory
.
Maps
Locked
Stores the amount
and end
block for an account’s lock.
PointHistory
Stores the global bias
, slope
and height
at a particular point in history.
UserPointHistory
Stores the bias
, slope
and height
for an account at a particular point in history.
UserPointEpoch
Stores the current epoch for an account.
SlopeChanges
Stores scheduled changes of slopes for ending locks.
Structs
LockedBalance
The amount
and end
height for a locked balance.
Parameter |
Type |
Description |
---|---|---|
|
Balance |
The amount deposited to receive vote-escrowed tokens. |
|
BlockNumber |
The end height after which the balance can be unlocked. |
Point
The bias
, slope
and height
for our linear function.
Parameter |
Type |
Description |
---|---|---|
|
Balance |
The bias for the linear function. |
|
Balance |
The slope for the linear function. |
|
BlockNumber |
The current block height when this point was stored. |
External Functions
create_lock
Create a lock on the account’s balance to expire in the future.
Specification
Function Signature
create_lock(who, amount, unlock_height)
Parameters
who
: The user’s address.amount
: The amount to be locked.unlock_height
: The height to lock until.
Events
Preconditions
The function call MUST be signed by
who
.The
amount
MUST be non-zero.The account’s
old_locked.amount
MUST be non-zero.The
unlock_height
MUST be greater thannow
.The
unlock_height
MUST NOT be greater thannow + MaxPeriod
.
Postconditions
The account’s
LockedBalance
MUST be set as follows:new_locked.amount
: MUST be theamount
.new_locked.end
: MUST be theunlock_height
.
The
UserPointEpoch
MUST increase by one.A new
Point
MUST be recorded at this epoch:slope = amount / max_period
bias = slope * (unlock_height - now)
height = now
Function withdrawStake MUST complete successfully using the account’s total stake.
Function depositStake MUST complete successfully using the current balance (balance_at).
increase_amount
Deposit additional tokens for a pre-existing lock to improve voting power.
Specification
Function Signature
increase_amount(who, amount)
Parameters
who
: The user’s address.amount
: The amount to be locked.
Events
Preconditions
The function call MUST be signed by
who
.The
amount
MUST be non-zero.The account’s
old_locked.amount
MUST be non-zero.The account’s
old_locked.end
MUST be greater thannow
.
Postconditions
The account’s
LockedBalance
MUST be set as follows:new_locked.amount
: MUST beold_locked.amount + amount
.new_locked.end
: MUST be theold_locked.end
.
The
UserPointEpoch
MUST increase by one.A new
Point
MUST be recorded at this epoch:slope = new_locked.amount / max_period
bias = slope * (new_locked.end - now)
height = now
extend_unlock_height
Push back the expiry on a pre-existing lock to retain voting power.
Specification
Function Signature
extend_unlock_height(who, unlock_height)
Parameters
who
: The user’s address.unlock_height
: The new expiry deadline.
Events
Preconditions
The function call MUST be signed by
who
.The
amount
MUST be non-zero.The account’s
old_locked.amount
MUST be non-zero.The account’s
old_locked.end
MUST be greater thannow
.The
unlock_height
MUST be greater thanold_locked.end
.The
unlock_height
MUST NOT be greater thannow + MaxPeriod
.
Postconditions
The account’s
LockedBalance
MUST be set as follows:new_locked.amount
: MUST beold_locked.amount
.new_locked.end
: MUST be theunlock_height
.
The
UserPointEpoch
MUST increase by one.A new
Point
MUST be recorded at this epoch:slope = new_locked.amount / max_period
bias = slope * (new_locked.end - now)
height = now
withdraw
Remove the lock on an account to allow access to the account’s funds.
Specification
Function Signature
withdraw(who)
Parameters
who
: The user’s address.
Events
Preconditions
The function call MUST be signed by
who
.The account’s
old_locked.amount
MUST be non-zero.The current height (
now
) MUST be greater than or equal toold_locked.end
.
Postconditions
The account’s
LockedBalance
MUST be removed.Function withdrawStake MUST complete successfully using the account’s total stake.
Internal Functions
balance_at
Using the Point
, we can calculate the current voting power (balance
) as follows:
balance = point.bias - (point.slope * (height - point.height))
Specification
Function Signature
balance_at(who, height)
Parameters
who
: The user’s address.height
: The future height.
Preconditions
The
height
MUST be>= point.height
.
Events
Deposit
Emit an event if a user successfully deposited tokens or increased the lock time.
Event Signature
Deposit(who, amount, unlock_height)
Parameters
who
: The user’s account identifier.amount
: The amount locked.unlock_height
: The height to unlock after.
Functions
Withdraw
Emit an event if a user withdrew previously locked tokens.
Event Signature
Withdraw(who, amount)
Parameters
who
: The user’s account identifier.amount
: The amount unlocked.
Functions