PRIV ProtocolPRIV Docs
Contracts

TokenDistributionV2

Multi-tier token vesting and distribution contract for PRIV Protocol allocations.

TokenDistributionV2

The TokenDistributionV2 contract manages PRIV token allocations and vesting schedules for the Founder, Treasury, and Ecosystem Rewards allocations.


Overview

Token Allocation (100M Total Supply)

AllocationAmountTGE UnlockCliffVesting
Founder30M (30%)3M (10%)6 months24 months linear
Treasury13M (13%)6.5M (50%)None12 months linear
Ecosystem Rewards25M (25%)0NoneReleased via rewards
Presale15M (15%)20% (3M)-6 months linear (via PrivPresale contract)
Liquidity10M (10%)100%-Not in this contract
Reserve5M (5%)--Not in this contract

This contract manages 70M tokens. The remaining 30M (15M Presale + 10M Liquidity + 5M Reserve) are handled separately.


Allocation Types

enum AllocationType {
    Founder,          // 30M: 3M TGE, 6mo cliff, 24mo vest
    Treasury,         // 13M: 6.5M TGE, 12mo vest
    EcosystemRewards  // 25M: Released via rewards contracts
}

Constants

Founder Allocation

ConstantValueDescription
FOUNDER_TOTAL30,000,000 PRIVTotal founder allocation
FOUNDER_TGE3,000,000 PRIVTGE unlock (10%)
FOUNDER_CLIFF6 monthsCliff duration
FOUNDER_VESTING_DURATION24 monthsLinear vesting after cliff

Treasury Allocation

ConstantValueDescription
TREASURY_TOTAL13,000,000 PRIVTotal treasury allocation
TREASURY_TGE6,500,000 PRIVTGE unlock (50%)
TREASURY_CLIFF0No cliff
TREASURY_VESTING_DURATION12 monthsLinear vesting

Ecosystem Rewards

ConstantValueDescription
ECOSYSTEM_TOTAL25,000,000 PRIVTotal ecosystem allocation
ECOSYSTEM_TGE0No TGE unlock
ECOSYSTEM_VESTING_DURATION0Released via rewards

General

ConstantValueDescription
TOTAL_DISTRIBUTION70,000,000 PRIVTotal managed by contract
BENEFICIARY_TIMELOCK2 daysTimelock for beneficiary changes

Functions

Initialization

initializeAllocations

Initializes all token allocations. Can only be called once.

function initializeAllocations() external onlyOwner

Requirements:

  • Contract must not be already initialized
  • Contract must hold at least 70M PRIV tokens

Events:

event AllocationsInitialized(uint256 tgeTimestamp);

Beneficiary Management

setBeneficiary

Sets the initial beneficiary for an allocation (one-time only).

function setBeneficiary(
    AllocationType allocationType,
    address beneficiary
) external onlyOwner

Requirements:

  • Beneficiary must not already be set
  • Address must not be zero

proposeBeneficiaryChange

Proposes changing a beneficiary (with 2-day timelock).

function proposeBeneficiaryChange(
    AllocationType allocationType,
    address newBeneficiary
) external

Requirements:

  • Caller must be current beneficiary or owner
  • New beneficiary must not be zero address

Events:

event BeneficiaryChangeProposed(
    AllocationType indexed allocationType,
    address indexed currentBeneficiary,
    address indexed newBeneficiary,
    uint256 effectiveTime
);

executeBeneficiaryChange

Executes a pending beneficiary change after timelock.

function executeBeneficiaryChange(AllocationType allocationType) external

cancelBeneficiaryChange

Cancels a pending beneficiary change.

function cancelBeneficiaryChange(AllocationType allocationType) external

Claiming

claim

Claims all currently vested tokens for an allocation.

function claim(AllocationType allocationType) external nonReentrant

Requirements:

  • Contract must be initialized
  • Caller must be the beneficiary
  • Must have claimable tokens

Events:

event TokensClaimed(
    AllocationType indexed allocationType,
    address indexed beneficiary,
    uint256 amount
);

View Functions

getClaimable

Returns the amount currently claimable for an allocation.

function getClaimable(AllocationType allocationType) public view returns (uint256)

getAllocationStatus

Returns comprehensive status for an allocation.

function getAllocationStatus(AllocationType allocationType) external view returns (
    uint256 totalAmount,
    uint256 vestedAmount,
    uint256 claimedAmount,
    uint256 claimableAmount,
    uint256 vestingStart,
    uint256 cliffEnd,
    uint256 vestingEnd,
    address beneficiary
)

getPendingBeneficiaryChange

Returns pending beneficiary change details.

function getPendingBeneficiaryChange(AllocationType allocationType) external view returns (
    address newBeneficiary,
    uint256 effectiveTime,
    uint256 timeRemaining
)

getAllAllocations

Returns all three allocation structs.

function getAllAllocations() external view returns (Allocation[3] memory)

getTotalClaimed / getTotalRemaining

Returns aggregate claiming statistics.

function getTotalClaimed() external view returns (uint256)
function getTotalRemaining() external view returns (uint256)

Vesting Schedule Visualization

Founder (30M)

TGE                  6 months               30 months
|                      |                       |
v                      v                       v
[3M TGE]---[CLIFF]---[===== 27M LINEAR =====]

At TGE:     3M unlocked
Month 6:    3M (cliff ends, vesting begins)
Month 12:   3M + 6.75M = 9.75M
Month 18:   3M + 13.5M = 16.5M
Month 24:   3M + 20.25M = 23.25M
Month 30:   30M (fully vested)

Treasury (13M)

TGE                                     12 months
|                                          |
v                                          v
[6.5M TGE]---[======= 6.5M LINEAR =======]

At TGE:     6.5M unlocked
Month 6:    6.5M + 3.25M = 9.75M
Month 12:   13M (fully vested)

Ecosystem (25M)

TGE
|
v
[========= 25M AVAILABLE FOR REWARDS =========]

All 25M available immediately for rewards distribution.

Integration Example

import { ethers } from 'ethers';

const distribution = new ethers.Contract(address, abi, signer);

// Check allocation status
const status = await distribution.getAllocationStatus(0); // Founder
console.log('Total:', ethers.formatEther(status.totalAmount));
console.log('Vested:', ethers.formatEther(status.vestedAmount));
console.log('Claimed:', ethers.formatEther(status.claimedAmount));
console.log('Claimable:', ethers.formatEther(status.claimableAmount));

// Claim tokens (as beneficiary)
const claimable = await distribution.getClaimable(0); // Founder
if (claimable > 0) {
  const tx = await distribution.claim(0);
  await tx.wait();
}

// Propose beneficiary change (2-day timelock)
const tx = await distribution.proposeBeneficiaryChange(
  0, // Founder allocation
  newBeneficiaryAddress
);
await tx.wait();

// After 2 days, execute the change
await distribution.executeBeneficiaryChange(0);

Security Features

FeatureDescription
ReentrancyGuardProtection against reentrancy attacks
Beneficiary Timelock2-day delay for beneficiary changes
One-Time InitAllocations can only be initialized once
SafeERC20Safe token transfer handling
Balance CheckRequires full balance before initialization

Events Reference

EventDescription
AllocationsInitializedContract initialized with TGE timestamp
TokensClaimedTokens claimed by beneficiary
BeneficiarySetInitial beneficiary set
BeneficiaryChangeProposedBeneficiary change proposed
BeneficiaryChangeCancelledPending change cancelled
BeneficiaryChangedBeneficiary change executed