Skip to main content

💸 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;
}
ParameterDescription
vestingStartTimeTimestamp of the start of the vesting
beneficiaryThe address that will eventually receive vested assets
vestingTokenThe address of the vested token
vestingAmountThe amount the user wants to put in vesting
paidAmountThe amount paid to the beneficiary at the current time
scheduleIdThe 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;
}
ParameterDescriptionExample value
secondsInPeriodThe duration of each vesting period in seconds86400 seconds for 1 day
durationInPeriodsThe total number of periods for the vesting20 for 20 days
cliffInPeriodsThe number of periods before the vesting starts3 for 3 days
exponentThe exponent for the vesting calculation1 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:

FunctionDescription
withdrawFromVestingWithdraws funds from a vesting contract
getScheduleRetrieves a schedule by ID
getVestingRetrieves vesting data by ID
getVestingsRetrieves all vesting data for a beneficiary
getVestingIdsRetrieves all vesting IDs for a beneficiary
getVestedAmountRetrieves the vested amount for a vesting ID
getWithdrawableAmountRetrieves 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:

FunctionDescription
createScheduleCreates a new vesting schedule with a custom exponent
createVestingCreates a new vesting
vestingCalculationPerforms 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