Skip to content
Draft

init #20

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions packages/hardhat/contracts/claim/GeneratePeriodicTranches.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

contract TrancheVesting {
struct Tranche {
uint256 time;
uint256 vestedFraction;
uint256 date;
}

function generatePeriodicTranches(uint256 startTime, uint256 cliffTime, uint256 endTime, string memory units) external pure returns (Tranche[] memory) {
uint256 totalTranches = getDifference(startTime, endTime, units);

Tranche[] memory tranches = new Tranche[](totalTranches);

uint256 currentIndex = 0;

for (uint256 i = 0; i < totalTranches; i++) {
uint256 trancheStartTime = addToDateTime(startTime, i, units);

if (trancheStartTime < cliffTime) {
continue;
}

uint256 vestedFraction = ((i + 1) * (100 / totalTranches)) * 100;

tranches[currentIndex] = Tranche({
time: trancheStartTime,
vestedFraction: vestedFraction,
date: trancheStartTime
});

currentIndex++;
}

return tranches;
}

// Function to calculate the difference between two timestamps
function getDifference(uint256 startTime, uint256 endTime, string memory units) internal pure returns (uint256) {
// Implementation of getDifference based on units
// You need to implement this function according to your requirements
}

// Function to add time to a given timestamp
function addToDateTime(uint256 timestamp, uint256 amount, string memory units) internal pure returns (uint256) {
// Implementation of addToDateTime based on units
// You need to implement this function according to your requirements
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import { PerAddressTrancheVesting, Tranche } from './abstract/PerAddressTrancheVesting.sol';
import { MerkleSet } from './abstract/MerkleSet.sol';

contract PerAddressTrancheVestingMerkle is PerAddressTrancheVesting, MerkleSet {
constructor(
IERC20 _token,
uint256 _total,
string memory _uri, // information on the sale (e.g. merkle proofs)
uint256 _voteFactor,
uint256 _start,
uint256 _end,
uint256 _cliff,
bytes32 _merkleRoot,
uint160 _maxDelayTime // the maximum delay time for the fair queue
)
PerAddressTrancheVesting(
_token,
_total,
_uri,
_voteFactor,
_start,
_end,
_cliff,
_maxDelayTime,
uint160(uint256(_merkleRoot))
)
MerkleSet(_merkleRoot)
{}

function NAME() external pure override returns (string memory) {
return 'PerAddressTrancheVestingMerkle';
}

function VERSION() external pure override returns (uint256) {
return 3;
}

function initializeDistributionRecord(
uint256 index, // the beneficiary's index in the merkle root
address beneficiary, // the address that will receive tokens
uint256 amount, // the total claimable by this beneficiary
bytes32[] calldata merkleProof
)
external
validMerkleProof(keccak256(abi.encodePacked(index, beneficiary, amount)), merkleProof)
{
_initializeDistributionRecord(beneficiary, amount);
}

function claim(
uint256 index, // the beneficiary's index in the merkle root
address beneficiary, // the address that will receive tokens
uint256 totalAmount, // the total claimable by this beneficiary
bytes32[] calldata merkleProof
)
external
validMerkleProof(keccak256(abi.encodePacked(index, beneficiary, totalAmount)), merkleProof)
nonReentrant
{
// effects
uint256 claimedAmount = _executeClaim(beneficiary, totalAmount);
// interactions
_settleClaim(beneficiary, claimedAmount);
}

function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
_setMerkleRoot(_merkleRoot);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { Distributor, AdvancedDistributor } from "./AdvancedDistributor.sol";
import { IContinuousVesting } from "../../interfaces/IContinuousVesting.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

abstract contract ContinuousVesting is AdvancedDistributor, IContinuousVesting {
uint256 private start; // time vesting clock begins
uint256 private cliff; // time vesting begins (all tokens vested prior to the cliff are immediately claimable)
uint256 private end; // time vesting clock ends

constructor(
IERC20 _token,
uint256 _total,
string memory _uri,
uint256 _voteFactor,
uint256 _start,
uint256 _cliff,
uint256 _end,
uint160 _maxDelayTime,
uint160 _salt
)
// use a large fraction denominator to provide the highest resolution on continuous vesting.
AdvancedDistributor(_token, _total, _uri, _voteFactor, 10**18, _maxDelayTime, _salt)
{
// require(_start <= _cliff, "vesting cliff before start");
// require(_cliff <= _end, "vesting end before cliff");
// require(_end <= 4102444800, "vesting ends after 4102444800 (Jan 1 2100)");

// start = _start;
// cliff = _cliff;
// end = _end;

emit SetContinuousVesting(start, cliff, end);
}

function getVestedFraction(
address beneficiary,
uint256 time, // time is in seconds past the epoch (e.g. block.timestamp)
uint256 _start,
uint256 _cliff,
uint256 _end

) public view returns (uint256) {
uint256 delayedTime = time- getFairDelayTime(beneficiary);
// no tokens are vested
if (delayedTime <= cliff) {
return 0;
}

// all tokens are vested
if (delayedTime >= end) {
return fractionDenominator;
}

// some tokens are vested
return (fractionDenominator * (delayedTime - start)) / (end - start);
}

function getVestingConfig()
external
view
returns (
uint256,
uint256,
uint256
)
{
// We probably need to update this so that it takes in an address
return (start, cliff, end);
}

// Adjustable admin functions
function setVestingConfig(
uint256 _start,
uint256 _cliff,
uint256 _end
) external onlyOwner {
start = _start;
cliff = _cliff;
end = _end;
// We probably don't need this function anymore, as it'll be per address
emit SetContinuousVesting(start, cliff, end);
}
}
121 changes: 121 additions & 0 deletions packages/hardhat/contracts/claim/abstract/PerAddressTrancheVesting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { AdvancedDistributor } from './AdvancedDistributor.sol';
import { IPerAddressTrancheVesting, Tranche } from '../../interfaces/IPerAddressTrancheVesting.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import "hardhat/console.sol";

/**
* @title TrancheVesting
* @notice Distributes funds to beneficiaries over time in tranches.
*/
abstract contract PerAddressTrancheVesting is AdvancedDistributor, IPerAddressTrancheVesting {
// time and vested fraction must monotonically increase in the tranche array
Tranche[] private tranches;

constructor(
IERC20 _token,
uint256 _total,
string memory _uri,
uint256 _voteFactor,
uint256 _start,
uint256 _end,
uint256 _cliff,
uint160 _maxDelayTime,
uint160 _salt
) AdvancedDistributor(_token, _total, _uri, _voteFactor, 10000, _maxDelayTime, _salt) {
// tranches can be set in the constructor or in setTranches()
_setTranches(_start, _end, _cliff);
}

/**
* @notice Get the vested fraction for a beneficiary at a given time.
* @dev Before the first tranche time, the vested fraction will be 0. At times between
* tranche_i and tranche_i+1, the vested fraction will be tranche_i+1's vested fraction.
* After the last tranche time, the vested fraction will be the fraction denominator.
*/
function getVestedFraction(
address beneficiary,
uint256 time,
uint256 _start,
uint256 _end,
uint256 _cliff
) public view returns (uint256) {
// tranches = calculate the the tranche from start, end, cliff from record, record.start, record.end, record.cliff,
// calculateTranche(beneficiary, record);

uint256 delay = getFairDelayTime(beneficiary);
for (uint256 i = tranches.length; i != 0; ) {
unchecked {
--i;
}

if (time - delay > tranches[i].time) {
return tranches[i].vestedFraction;
}
}

return 0;
}

// Get a single tranche
function getTranche(uint256 i) public view returns (Tranche memory) {
return tranches[i];
}

// Get all tranches
function getTranches() public view returns (Tranche[] memory) {
return tranches;
}

/**
* @dev Tranches can be updated at any time. The quantity of tokens a user can claim is based on the
* claimable quantity at the time of the claim and the quantity of tokens already claimed by the user.
*/
function _setTranches(
uint256 _start,
uint256 _end,
uint256 _cliff
) private {

// TODO: create tranches based off start, end, and cliff
Tranche[] memory _tranches = new Tranche[](3);

require(_tranches.length != 0, 'tranches required');

delete tranches;

uint128 lastTime = 0;
uint128 lastVestedFraction = 0;

for (uint256 i = 0; i < _tranches.length; ) {
require(_tranches[i].vestedFraction != 0, 'tranche vested fraction == 0');
require(_tranches[i].time > lastTime, 'tranche time must increase');
require(
_tranches[i].vestedFraction > lastVestedFraction,
'tranche vested fraction must increase'
);
lastTime = _tranches[i].time;
lastVestedFraction = _tranches[i].vestedFraction;
tranches.push(_tranches[i]);

emit SetTranche(i, lastTime, lastVestedFraction);

unchecked {
++i;
}
}

require(lastTime <= 4102444800, 'vesting ends after 4102444800 (Jan 1 2100)');
require(lastVestedFraction == fractionDenominator, 'last tranche must vest all tokens');
}

/**
* @notice Set the vesting tranches. Tranches must be sorted by time and vested fraction must monotonically increase.
* The last tranche must vest all tokens (vestedFraction == fractionDenominator)
*/
function setTranches(uint256 _start, uint256 _end, uint256 _cliff) external onlyOwner {
_setTranches(_start, _end, _cliff);
}
}
Loading