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.xw
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
- Standardization: All contracts deployed with consistent patterns
- Upgradeability: New contract versions can be deployed
- Gas Efficiency: Optimized deployment costs
- 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 }
);
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
- 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: []
};
}
const userVestingIds = await getAllRecipientVestings(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
};
}