Skip to main content

Vesting Contracts Overview

Vesting contracts are smart contracts that control the gradual release of tokens over time according to predefined schedules. TokenOps SDK provides a comprehensive suite of vesting solutions for different use cases, from employee compensation to investor token distributions.

What is Token Vesting?

Token vesting is a mechanism that locks tokens and releases them gradually over time according to a predefined schedule. This ensures long-term commitment from recipients and prevents immediate token dumps that could harm project value.

Key Benefits

  • Alignment of Interests: Recipients have incentive to support long-term project success
  • Market Stability: Prevents sudden large token releases that could crash prices
  • Compliance: Meets regulatory requirements for token distributions
  • Flexibility: Customizable schedules for different stakeholder groups

Vesting Contract Types

TokenOps SDK offers four main vesting contract variants to suit different needs:

1. Token Vesting Manager

Standard ERC20 token vesting

// Perfect for: Team tokens, investor distributions, advisor compensation
const manager = new TokenVestingManager(contractAddress, tokenAddress, provider);

Use Cases:

  • Employee equity compensation
  • Investor token distributions
  • Advisor and consultant payments
  • Partnership agreements

2. Native Token Vesting Manager

ETH and native token vesting

// Perfect for: ETH distributions, native token rewards
const nativeManager = new NativeTokenVestingManager(contractAddress, provider);

Use Cases:

  • ETH-based compensation
  • Native token rewards
  • Revenue sharing in ETH
  • Validator rewards

3. Token Vesting Manager with Votes

Governance-enabled vesting

// Perfect for: DAO governance tokens, voting rights preservation
const votingManager = new TokenVestingManagerVotes(contractAddress, tokenAddress, provider);

Use Cases:

  • DAO governance tokens
  • Voting rights during vesting
  • Community governance participation
  • Delegated voting mechanisms

4. Vested Milestone Manager

Achievement-based token releases

// Perfect for: Performance-based compensation, project milestones
const milestoneManager = new VestedMilestoneManager(contractAddress, tokenAddress, provider);

Use Cases:

  • Performance-based bonuses
  • Project milestone rewards
  • Achievement-based unlocks
  • Conditional token releases

Vesting Schedule Components

All vesting schedules in TokenOps SDK share common components:

Timeline Components

interface VestingSchedule {
startTimestamp: bigint; // When vesting begins
endTimestamp: bigint; // When vesting completes
cliffReleaseTimestamp: bigint; // When cliff period ends
releaseIntervalSecs: bigint; // Seconds between releases
timelock: bigint; // Additional holding period after vesting
}

Amount Components

interface VestingAmounts {
cliffAmount: bigint; // Tokens released at cliff
linearVestAmount: bigint; // Tokens for linear vesting
initialUnlock: bigint; // Immediate unlock amount
}

Control Components

interface VestingControls {
isRevocable: boolean; // Can vesting be cancelled?
recipient: Address; // Who receives tokens
vestingId: string; // Unique vesting identifier (bytes32)
}

Vesting Schedule Types

1. Linear Vesting

Tokens are released continuously over time.

Example: 1000 tokens over 12 months = ~83.33 tokens/month

2. Cliff Vesting

No tokens released until a specific date, then a large amount unlocks.

Example: 0 tokens for 6 months, then 500 tokens unlock, then 500 tokens over 6 months

3. Step Vesting

Tokens are released in discrete intervals.

Example: 250 tokens every 3 months for 12 months

4. Milestone Vesting

Tokens are released when specific conditions are met.

Example: 333 tokens after each product launch milestone

Factory Pattern Architecture

TokenOps uses a factory pattern for deploying vesting contracts:

Benefits of Factory Pattern

  1. Standardization: All contracts deployed with consistent patterns
  2. Upgradeability: New contract versions can be deployed
  3. Gas Efficiency: Optimized deployment costs
  4. Security: Audited factory reduces individual contract risks

Funding Types

TokenOps vesting supports two funding models:

Full Funding (Type 0)

const fundingType = BigInt(0); // Full funding required upfront

// Must fund entire vesting amount before creating schedules
// More secure - ensures all tokens are available
// Prevents over-allocation

Partial Funding (Type 1)

const fundingType = BigInt(1); // Partial funding allowed

// Can create vesting schedules before full funding
// Better cash flow management
// Allows iterative funding

Common Use Cases

1. Employee Compensation

// 4-year vesting with 1-year cliff
const employeeVesting = {
startTimestamp: BigInt(Math.floor(Date.now() / 1000)),
endTimestamp: BigInt(Math.floor(Date.now() / 1000) + (4 * 365 * 24 * 60 * 60)),
cliffReleaseTimestamp: BigInt(Math.floor(Date.now() / 1000) + (365 * 24 * 60 * 60)),
cliffAmount: BigInt('25000000000000000000000'), // 25,000 tokens at cliff
linearVestAmount: BigInt('75000000000000000000000'), // 75,000 tokens linear
releaseIntervalSecs: BigInt(30 * 24 * 60 * 60), // Monthly releases
isRevocable: true // Can be revoked if employee leaves
};

2. Investor Distribution

// 2-year linear vesting with 6-month cliff
const investorVesting = {
startTimestamp: BigInt(Math.floor(Date.now() / 1000)),
endTimestamp: BigInt(Math.floor(Date.now() / 1000) + (2 * 365 * 24 * 60 * 60)),
cliffReleaseTimestamp: BigInt(Math.floor(Date.now() / 1000) + (6 * 30 * 24 * 60 * 60)),
cliffAmount: BigInt('500000000000000000000000'), // 500,000 tokens at cliff
linearVestAmount: BigInt('1500000000000000000000000'), // 1,500,000 tokens linear
releaseIntervalSecs: BigInt(7 * 24 * 60 * 60), // Weekly releases
isRevocable: false // Cannot be revoked
};

3. Advisor Compensation

// 18-month vesting with immediate partial unlock
const advisorVesting = {
startTimestamp: BigInt(Math.floor(Date.now() / 1000)),
endTimestamp: BigInt(Math.floor(Date.now() / 1000) + (18 * 30 * 24 * 60 * 60)),
cliffReleaseTimestamp: BigInt(0), // No cliff
cliffAmount: BigInt(0),
linearVestAmount: BigInt('90000000000000000000000'), // 90,000 tokens linear
initialUnlock: BigInt('10000000000000000000000'), // 10,000 tokens immediate
releaseIntervalSecs: BigInt(30 * 24 * 60 * 60), // Monthly releases
isRevocable: true
};

4. Milestone-Based Rewards

// Achievement-based milestone vesting
const milestones = [
{
description: "Launch Mainnet",
amount: BigInt('1000000000000000000000000'), // 1M tokens
completed: false
},
{
description: "Reach 100K Users",
amount: BigInt('2000000000000000000000000'), // 2M tokens
completed: false
},
{
description: "Launch Governance",
amount: BigInt('3000000000000000000000000'), // 3M tokens
completed: false
}
];

Core Operations

Creating Vesting Schedules

// Standard token vesting
const vestingResult = await manager.createVesting(
recipientAddress, // Address receiving tokens
startTimestamp, // When vesting starts
endTimestamp, // When vesting ends
timelock, // Additional lock period
initialUnlock, // Immediate unlock amount
cliffReleaseTimestamp, // When cliff ends
cliffAmount, // Amount released at cliff
releaseIntervalSecs, // Time between releases
linearVestAmount, // Amount for linear vesting
isRevocable, // Can be revoked
{ account: creatorAddress, amount: totalAmount }
);

const vestingId = vestingResult.vestingId; // Returns string (bytes32)

Claiming Tokens

// Claim vested tokens
const claimResult = await manager.claim(
vestingId, // Vesting schedule ID
{ account: recipientAddress, amount: gasFee }
);

Reading Vesting Information

// Get complete vesting information
const vestingInfo = await manager.getVestingInfo(vestingId);

// Get claimable amount
const claimable = await manager.getClaimableAmount(vestingId);

// Get vested amount at specific time
const vested = await manager.getVestedAmount(vestingId, timestamp);

// Get basic vesting data
const vestingData = await manager.vestingById(vestingId);

Event System

All vesting contracts emit comprehensive events for tracking:

// Vesting creation event
interface VestingCreatedEvent {
vestingId: string; // bytes32 ID
recipient: Address; // Token recipient
vesting: any; // Complete vesting data
}

// Token claim event
interface ClaimedEvent {
vestingId: string; // bytes32 ID
recipient: Address; // Who claimed
withdrawalAmount: bigint; // Amount claimed
}

// Vesting transfer event
interface VestingTransferredEvent {
previousOwner: Address; // Previous owner
newOwner: Address; // New owner
vestingId: string; // Vesting ID transferred
}

// Vesting revocation event
interface VestingRevoked {
vestingId: string; // Revoked vesting ID
numTokensWithheld: bigint; // Tokens returned to admin
vesting: any; // Vesting data
}

Administrative Functions

Funding Management

// Fund a vesting schedule (for partial funding type)
const fundResult = await manager.fundVesting(
vestingId, // Vesting to fund
fundingAmount, // Amount to add
{ account: adminAddress, amount: fundingAmount }
);

// Check funding status
const fundingInfo = await manager.getVestingFundingInfo(vestingId);
const isFullyFunded = await manager.isVestingFullyFunded(vestingId);

Vesting Transfers

// Direct transfer of vesting ownership
const transferResult = await manager.directVestingTransfer(
vestingId, // Vesting to transfer
newOwnerAddress, // New recipient
{ account: currentOwner }
);

// Cancel pending transfer
await manager.cancelVestingTransfer(vestingId, { account: currentOwner });

Batch Operations

// Create multiple vestings in one transaction
const batchResult = await manager.createVestingBatch(
recipientsArray, // Array of recipients
startTimestampsArray, // Array of start times
endTimestampsArray, // Array of end times
timelocksArray, // Array of timelocks
initialUnlocksArray, // Array of initial unlocks
cliffTimestampsArray, // Array of cliff times
cliffAmountsArray, // Array of cliff amounts
intervalsArray, // Array of release intervals
linearAmountsArray, // Array of linear amounts
revocableArray, // Array of revocable flags
{ account: creatorAddress, amount: totalFunding }
);

// Batch admin claim for multiple vestings
await manager.batchAdminClaim(
vestingIdsArray, // Array of vesting IDs
{ account: adminAddress, amount: gasFee }
);

Best Practices

1. Design Patterns

  • Use factory pattern for standardized deployments
  • Choose appropriate funding type for your use case
  • Plan vesting schedules carefully with proper cliff periods
  • Document all parameters and conditions clearly

2. Security

  • Use revocable vesting for employees (protection against departure)
  • Use non-revocable for investors (protection against arbitrary revocation)
  • Implement proper access controls for admin functions
  • Validate all input parameters before contract creation

3. Gas Optimization

  • Batch similar operations when possible
  • Use appropriate release intervals (not too frequent)
  • Consider funding type impact on gas costs
  • Monitor contract interaction costs

4. User Experience

  • Provide clear vesting progress displays
  • Send notifications for claimable tokens
  • Explain revocation conditions upfront
  • Implement intuitive claiming interfaces

Integration Examples

Vesting Dashboard

interface VestingDashboard {
totalVestings: number;
totalAllocated: bigint;
totalClaimed: bigint;
totalClaimable: bigint;
activeVestings: VestingInfo[];
}

async function getVestingDashboard(
manager: TokenVestingManager,
userAddress: Address
): Promise<VestingDashboard> {
// Get all recipients and filter for user
const allRecipients = await manager.getAllRecipients();
const isRecipient = await manager.isRecipient(userAddress);

if (!isRecipient) {
return {
totalVestings: 0,
totalAllocated: BigInt(0),
totalClaimed: BigInt(0),
totalClaimable: BigInt(0),
activeVestings: []
};
}

// You would need to track vesting IDs for each user
// This could be done through event monitoring
const userVestingIds = await getUserVestingIds(userAddress);

let totalAllocated = BigInt(0);
let totalClaimed = BigInt(0);
let totalClaimable = BigInt(0);
const activeVestings = [];

for (const vestingId of userVestingIds) {
const vestingInfo = await manager.getVestingInfo(vestingId);
const claimable = await manager.getClaimableAmount(vestingId);

totalAllocated += vestingInfo.cliffAmount + vestingInfo.linearVestAmount;
totalClaimed += vestingInfo.claimedAmount;
totalClaimable += claimable;

activeVestings.push({
vestingId,
...vestingInfo,
claimableAmount: claimable
});
}

return {
totalVestings: userVestingIds.length,
totalAllocated,
totalClaimed,
totalClaimable,
activeVestings
};
}

Event Monitoring

// Monitor vesting events for real-time tracking
manager.on('VestingCreated', (event) => {
console.log('New vesting created:', {
vestingId: event.vestingId,
recipient: event.recipient,
vesting: event.vesting
});
});

manager.on('Claimed', (event) => {
console.log('Tokens claimed:', {
vestingId: event.vestingId,
recipient: event.recipient,
amount: event.withdrawalAmount
});

// Send notification to recipient
sendClaimNotification(event.recipient, event.withdrawalAmount);
});

manager.on('VestingTransferred', (event) => {
console.log('Vesting ownership transferred:', {
previousOwner: event.previousOwner,
newOwner: event.newOwner,
vestingId: event.vestingId
});
});