💸 Vesting
Introduction
The main concept of the Vesting module is to lock ERC20 tokens for the predetermined amount of time before allowing token holders fully access their asset.
Implementation
The AVesting contract manages vestings and associated schedules for multiple beneficiaries and ERC20 tokens.
This contract stands out for its flexibility, offering support for both linear and exponential vesting calculations out of the box.
Linear vesting has a constant release rate over time (exponent = 1), resulting in a linear graph.
Exponential vesting allows for a more flexible release rate, defined by the exponent.
Higher exponents result in a steeper release curve.
The current vesting contract implementation supports:
- Multiple vestings
- Multiple beneficiaries (one beneficiary for one vesting)
- Multiple ERC20 tokens (one token per vesting)
- Linear calculation
- Exponential calculation
- Customizable cliff
Vesting formula is as follows:

where:
- vestedAmount - the calculated vested amount
- elapsedPeriodsPercentage - the percentage of passed periods
- exponent - the exponent number that is set in
scheduleStruct - totalVestingAmount - the amount vested initially
Vesting contract has two main components: Schedule struct and Vesting struct.
struct VestingData {
uint256 vestingStartTime;
address beneficiary;
address vestingToken;
uint256 vestingAmount;
uint256 paidAmount;
uint256 scheduleId;
}
| Parameter | Description |
|---|---|
vestingStartTime | Timestamp of the start of the vesting |
beneficiary | The address that will eventually receive vested assets |
vestingToken | The address of the vested token |
vestingAmount | The amount the user wants to put in vesting |
paidAmount | The amount paid to the beneficiary at the current time |
scheduleId | The ID of the associated schedule |
Each vesting contains scheduleId, which is associated with a schedule struct.
struct BaseSchedule {
uint256 secondsInPeriod;
uint256 durationInPeriods;
uint256 cliffInPeriods;
}
struct Schedule {
BaseSchedule scheduleData;
uint256 exponent;
}
| Parameter | Description | Example value |
|---|---|---|
secondsInPeriod | The duration of each vesting period in seconds | 86400 seconds for 1 day |
durationInPeriods | The total number of periods for the vesting | 20 for 20 days |
cliffInPeriods | The number of periods before the vesting starts | 3 for 3 days |
exponent | The exponent for the vesting calculation | 1 for linear vesting, >=2 for exponential. It's not possible to set the exponent as 0 |
You can create as many Schedules as needed with different parameters with an associated scheduleId.
Then a schedule can be assigned to vestings. So it's possible to create multiple vestings with the same schedule.
The contract includes the following public functions:
| Function | Description |
|---|---|
withdrawFromVesting | Withdraws funds from a vesting contract |
getSchedule | Retrieves a schedule by ID |
getVesting | Retrieves vesting data by ID |
getVestings | Retrieves all vesting data for a beneficiary |
getVestingIds | Retrieves all vesting IDs for a beneficiary |
getVestedAmount | Retrieves the vested amount for a vesting ID |
getWithdrawableAmount | Retrieves the withdrawable amount for a vesting ID |
Additionally, there are a couple of internal functions that allow creating vesting, schedule, and calculating the vesting amount:
| Function | Description |
|---|---|
createSchedule | Creates a new vesting schedule with a custom exponent |
createVesting | Creates a new vesting |
vestingCalculation | Performs the vesting calculation |
Example
Example of creating and calculating vesting.
Schedule memory schedule_ = Schedule({
scheduleData: BaseSchedule({
secondsInPeriod: 1 days, // 86400 seconds,
durationInPeriods: 20, // 20 days,
cliffInPeriods: 3 // 3 days
}),
exponent: 1 // linear vesting
});
// as this is the first schedule we create, the id will be 1
_createSchedule(schedule_);
// will be our beneficiary
address bob_ = address(0xb0b);
// vesting token
address usdt_ = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
VestingData memory vestingData_ = VestingData({
vestingStartTime: block.timestamp, // start vesting from the last block
beneficiary: bob_, // beneficiary
vestingToken: usdt_, // vesting token,
vestingAmount: 1000, // 1000 tokens,
paidAmount: 0, // paid tokens at the current time
scheduleId: 1 // id of the schedule
});
// create vesting
_createVesting(vestingData_);
vestingCalculation(1 days); // 1 day: 0 tokens
vestingCalculation(2 days); // 2 day: 0 tokens
vestingCalculation(3 days); // 3 day: 0 tokens
vestingCalculation(4 days); // 4 day: 200 tokens
vestingCalculation(5 days); // 5 day: 250 tokens
vestingCalculation(6 days); // 6 day: 300 tokens
vestingCalculation(7 days); // 7 day: 350 tokens
vestingCalculation(8 days); // 8 day: 400 tokens
vestingCalculation(9 days); // 9 day: 450 tokens
// ...
vestingCalculation(20 days); // 20 day: 1000 tokens