openzeppelin-contracts
openzeppelin-contracts copied to clipboard
Implement Twisted and Milestone Vesting
🧐 Motivation
The most basic type of vesting is linear when after the cliff period the fixed portions of token are released to the beneficiary on every time interval. It is already implemented in VestingWallet contract. Nonetheless, there are more types of vesting that are more helpful for tier 1 projects, advisors, VCs, and capitals: Twisted and Milestone vesting. More details below.
📝 Details
To convey the ideas in a very basic form, the twisted vesting consists of a combination of linear vesting schedules following one after another. Milestone vesting is the corner case of twisted vesting that only uses cliffs with zero length linear part (immediate vesting after a cliff). With twisted and milestone vesting it is possible to create complex non-linear vesting schedules in a gas optimized way. Hence, the benefits of the suggested vesting models are: (1) creating arbitrary non-linear schedules, (2) gas optimization, (3) having a single vesting account that shows the full balance in vesting for all schedules (especially helpful for complex non-linear cases).
There are two articles that describe Twisted and Milestone vesting and their use cases in detail:
- Token Vesting: The Complete Guide to Creating Vesting in Tokenomics
- TOKEN VESTING – EVERYTHING YOU NEED TO KNOW
I am open to contributing to this issue.
Ready to work with Keref merging with issue #4441 and/or with MerlinEgalite on issue #4131
Hi, to integrate a difference vesting schedule in my proposal is pretty simple, it is possible to override the _vestingStatus function to implement a different vesting schedule.
Here for our project we have an exponential decaying function.
/// @notice Calculates the lockedAmount/unlockedAmount amounts according to reverse exponential unlock
function _vestingStatus(uint256 vestedAmount, uint64 startTime)
internal view override returns (uint256 unlockedAmount, uint256 lockedAmount)
{
// penalty function is (x^1.4 = e^(1.4*ln(x)), with x = 1 - elapsed / vestingDuration
int128 elapsedRatio = ABDKMath64x64.divu(block.timestamp - startTime, vestingDuration());
int128 lnElapsedRatio = ABDKMath64x64.ln(ABDKMath64x64.sub(1 << 64, elapsedRatio));
int128 expo14 = int128(uint128(0x16666666666666666)); // 1.4 * 2**64
int128 penaltyExponent = ABDKMath64x64.mul(expo14, lnElapsedRatio);
int128 penaltyRatio = ABDKMath64x64.exp(penaltyExponent);
lockedAmount = ABDKMath64x64.mulu(penaltyRatio, vestedAmount);
unlockedAmount = vestedAmount - lockedAmount;
}
It's quite trivial to implement linear vesting with cliff.