WARNING: All code related to Lockups is unaudited. Use at your own risk.
The Lockup program provides a simple mechanism to lockup tokens of any mint, and release those funds over time as defined by a vesting schedule. Although these lockups track a target beneficiary, who will eventually receive the funds upon vesting, a proper deployment of the program will ensure this beneficiary can never actually retrieve tokens before vesting. Funds are never in an SPL token wallet owned by a user, and are completely program controlled.
There is a single account type used by the program.
Vesting
- An account defining a vesting schedule, realization condition, and vault holding the tokens to be released over time.
Lockup occurs when tokens are transferred into the program creating a Vesting
account on behalf of a beneficiary via the CreateVesting
instruction.
There are three parameters to specify:
- Start timestamp - unix timestamp (in seconds) of the time when vesting begins.
- End timestamp - unix timestamp (in seconds) of the time when all tokens will unlock.
- Period count - the amount of times vesting should occur.
- Deposit amount - the total amount to vest.
- Realizer - the program defining if and when vested tokens can be distributed to a beneficiary.
Together these parameters form a linearly unlocked vesting schedule. For example, if one wanted to lock 100 SPL tokens that unlocked twice, 50 each time, over the next year, one would use the following parameters (in JavaScript).
const startTimestamp = Date.now()/1000;
const endTimestamp = Date.now()/1000 + 60*60*24*365;
const periodCount = 2;
const depositAmount = 100 * 10**6; // 6 decimal places.
const realizer = null; // No realizer in this example.
From these five parameters, one can deduce the total amount vested at any given time.
Once created, a Vesting account's schedule cannot be mutated.
Withdrawing is straightforward. Simply invoke the Withdraw
instruction, specifying an
amount to withdraw from a Vesting account. The beneficiary of the
Vesting account must sign the transaction, but if enough time has passed for an
amount to be vested, and, if the funds are indeed held in the lockup program's vault
(a point mentioned below) then the program will release the funds.
Optionally, vesting accounts can be created with a realizer
program, which is
a program implementing the lockup program's RealizeLock
trait. In
addition to the vesting schedule, a realizer
program determines if and when a
beneficiary can ever seize control over locked funds. It's effectively a function
returning a boolean: is realized or not.
The uses cases for a realizer are application specific. For example, in the case of the staking program, when a vesting account is distributed as a reward, the staking program sets itself as the realizor, ensuring that the only way for the vesting account to be realized is if the beneficiary completely unstakes and incurs the unbonding timelock alongside any other consequences of unstaking (e.g., the inability to vote on governance proposals). This implies that, if one never unstakes, one never receives locked token rewards, adding an additional consideration when managing one's stake.
If no such realizer
exists, tokens are realized upon account creation.
Although funds cannot be freely withdrawn prior to vesting, they can be sent to/from other programs that are part of a Whitelist. These programs are completely trusted. Any bug or flaw in the design of a whitelisted program can lead to locked tokens being released ahead of schedule, so it's important to take great care when whitelisting any program.
This of course begs the question, who approves the whitelist? The Lockup program doesn't care. There simply exists an authority key that can, for example, be a democratic multisig, a single admin, or the zero address--in which case the authority ceases to exist, as the program will reject transactions signing from that address. Although the authority can never move a Vesting account's funds, whoever controls the authority key controls the whitelist. So when using the Lockup program, one should always be cognizant of it's whitelist governance, which ultimately anchors one's trust in the program, if any at all.
To create a whitelisted program that receives withdrawals/deposits from/to the Lockup program,
one needs to implement the whitelist transfer interface, which assumes nothing about the
instruction_data
but requires accounts to be provided in a specific order.
Take staking locked tokens as a working example.
Suppose you have a vesting account with some funds you want to stake.
First, one must add the staking Registry as a whitelisted program, so that the Lockup program
allows the movement of funds. This is done by the WhitelistAdd
instruction.
Once whitelisted, Vesting accounts can transfer funds out of the Lockup program and
into the Registry program by invoking the Lockup program's WhitelistWithdraw
instruction, which, other than access control, simply relays the instruction from the
Lockup program to the Registry program along with accounts, signing the
Cross-Program-Invocation (CPI) with the Lockup's program-derived-address to allow
the transfer of funds, which ultimately is done by the Registry. It is the Registry's responsibility
to track where these funds came from, keep them locked, and eventually send them back.
When creating this instruction on the client, there are two parameters to provide:
the maximum amount
available for transfer and the opaque CPI instruction_data
.
In the example, here, it would be the Borsh serialized instruction data for the
Registry's Deposit
instruction.
The other direction follows, similarly. One invokes the WhitelistDeposit
instruction
on the LockupProgram, relaying the transaction to the Registry, which ultimately
transfer funds back into the lockup program on behalf of the Vesting account.
Assuming the authority
account is set on the Lockup program, one can use this Whitelist
mechanism to do major version upgrades of the lockup program. One can whitelist the
new Lockup program, and then all Vesting accounts would invidiually perform the migration
by transferring their funds to the new proigram via the WhitelistWithdraw
instruction.