openzeppelin-contracts icon indicating copy to clipboard operation
openzeppelin-contracts copied to clipboard

Implement Twisted and Milestone Vesting

Open gregzaitsev opened this issue 1 year ago • 2 comments

🧐 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:

  1. Token Vesting: The Complete Guide to Creating Vesting in Tokenomics
  2. TOKEN VESTING – EVERYTHING YOU NEED TO KNOW

I am open to contributing to this issue.

gregzaitsev avatar Jul 28 '23 09:07 gregzaitsev

Ready to work with Keref merging with issue #4441 and/or with MerlinEgalite on issue #4131

gregzaitsev avatar Jul 28 '23 12:07 gregzaitsev

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.

Keref avatar Aug 26 '23 05:08 Keref