Token vesting and lockups are critical mechanisms for aligning long-term incentives in Web3 projects. A vesting schedule releases tokens to recipients linearly over time (e.g., 25% over 4 years), while a lockup imposes a hard period where tokens are completely non-transferable. These contracts are essential for team allocations, investor cliffs, and community airdrops to prevent immediate market dumping. Common standards include OpenZeppelin's VestingWallet and custom implementations using time-locks. The core security principle is that the release logic must be immutable and trustless once deployed.
How to Set Up Vesting and Lockups
How to Set Up Vesting and Lockups
A technical guide for developers on implementing secure token vesting and lockup schedules using smart contracts.
To implement a basic linear vesting contract, you can extend OpenZeppelin's libraries. Start by inheriting from VestingWallet in Solidity. The constructor requires the beneficiary address, a start timestamp for the vesting cliff, and the total duration. The contract will then calculate the releasable amount based on the elapsed time. For more complex schedules (e.g., with an initial cliff followed by monthly releases), you would implement a custom contract using Solidity's block.timestamp and safemath libraries to manage distributions securely and prevent reentrancy attacks.
Here is a simplified code example for a custom vesting contract with a one-year cliff:
solidity// SPDX-License-Identifier: MIT import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract TokenVester is ReentrancyGuard { IERC20 public immutable token; address public beneficiary; uint256 public startTime; uint256 public cliffDuration; uint256 public vestingDuration; uint256 public totalAllocation; uint256 public released; constructor(IERC20 _token, address _beneficiary, uint256 _cliff, uint256 _duration) { token = _token; beneficiary = _beneficiary; startTime = block.timestamp; cliffDuration = _cliff; vestingDuration = _duration; } function release() external nonReentrant { require(block.timestamp >= startTime + cliffDuration, "Cliff not passed"); uint256 elapsed = block.timestamp - startTime; uint256 totalVested = (totalAllocation * elapsed) / vestingDuration; uint256 unreleased = totalVested - released; released += unreleased; token.transfer(beneficiary, unreleased); } }
For production use, consider critical security practices. Always use pull-over-push payments, allowing beneficiaries to claim tokens rather than having the contract send them automatically, which reduces risk. Implement a multi-signature wallet or timelock controller as the contract owner for administrative functions. Thoroughly test vesting math for edge cases using frameworks like Foundry or Hardhat, simulating time jumps to verify distributions at the cliff and end date. Audit the contract for common vulnerabilities, especially around timestamp manipulation and rounding errors in division.
Several established protocols offer audited vesting solutions. Sablier V2 provides continuous, linear streaming of tokens. Superfluid enables real-time finance with constant flow agreements. For DAO treasury management, Llama offers sophisticated vesting and payroll modules. When choosing a solution, evaluate gas costs, flexibility of schedules, and compatibility with your token standard (ERC-20, ERC-721). For most teams, using or forking an audited contract from OpenZeppelin or a protocol like Sablier is safer than writing custom logic from scratch.
To deploy, first test the contract on a fork of mainnet or a testnet like Sepolia. Use a verification service like Sourcify or Etherscan to publish the source code. Clearly document the schedule parameters—start time, cliff, duration, and total amount—for all stakeholders. After deployment, the vesting contract should be immutable. For ongoing management, consider using a dashboard like Llama's admin interface or building a simple frontend that connects to the contract's releasableAmount view function, allowing beneficiaries to track and claim their tokens transparently.
How to Set Up Vesting and Lockups
A practical guide to implementing token vesting and lockup schedules using smart contracts, covering key concepts, security considerations, and deployment steps.
Token vesting and lockups are essential mechanisms for aligning long-term incentives in Web3 projects. A vesting schedule releases tokens to beneficiaries (e.g., team members, investors, advisors) linearly over time, while a lockup enforces a mandatory holding period before any tokens can be claimed. These contracts are critical for preventing token dumps that can destabilize a project's economy. Common implementations include cliff periods (an initial delay before vesting starts) and linear release (continuous unlocking). For security, these contracts are typically non-upgradeable and hold tokens in escrow, releasing them programmatically based on block timestamps.
Before writing any code, you must decide on the schedule parameters and the token standard. Key variables include the total allocation, vesting start time (often the TGE - Token Generation Event), cliff duration (e.g., 12 months), and total vesting period (e.g., 48 months). You'll also need to choose between a single beneficiary contract or a batch vesting contract for multiple users. The token itself must be an ERC-20 compatible asset on your chosen chain (Ethereum, Polygon, Arbitrum, etc.). Ensure you have a development environment set up with Hardhat or Foundry, and access to a testnet like Sepolia or Goerli for deployment testing.
The core logic involves calculating the releasable amount based on elapsed time. A typical Solidity function uses the formula: releasable = (totalAllocation * (block.timestamp - startTime)) / vestingDuration. This calculation must account for the cliff (if(block.timestamp < startTime + cliff) return 0;). Security is paramount: the contract should inherit from OpenZeppelin's Ownable or AccessControl for administration, use SafeERC20 for token transfers, and include emergency withdrawal functions only for the owner to rescue tokens in case of issues. Always implement a release() function that transfers the calculated amount to the beneficiary and updates the released balance.
For deployment, you will need the address of the vested token and the beneficiary address. Using Hardhat, a deployment script would deploy the vesting contract, transferring the total token allocation to it, and then renouncing ownership to make the schedule immutable. It is critical to verify the contract on a block explorer like Etherscan after deployment. For batch setups, consider using a factory pattern or a vesting wallet distributor contract like those provided in OpenZeppelin Contracts v4.9+. Always conduct thorough testing with unit tests that simulate the full vesting period and edge cases like early withdrawal attempts.
Post-deployment, you must monitor the contract. While the schedule is autonomous, you should track release events and ensure beneficiaries know how to claim. For DAOs or teams, transparently publishing the vesting contract addresses builds trust. Common pitfalls include incorrect timestamp handling (use block.timestamp, not now), insufficient gas for claims, and not accounting for token decimals. For more complex schedules (e.g., non-linear, milestone-based), you may need a custom solution or an audit from a firm like OpenZeppelin or Trail of Bits before mainnet deployment.
Core Concepts: Cliff, Linear, and Custom Schedules
Token vesting schedules are critical for aligning long-term incentives. This guide explains the mechanics of cliff, linear, and custom vesting models, with practical implementation examples.
A vesting schedule is a time-based mechanism that controls the release of locked tokens to recipients, such as team members, investors, or advisors. Its primary purpose is to prevent immediate sell pressure and align incentives by ensuring stakeholders remain committed to the project's long-term success. The schedule defines the rules for how and when tokens transition from a locked state to a claimable state. This is typically enforced by a smart contract on-chain, ensuring transparency and immutability. Common use cases include team allocations, investor token unlocks, and ecosystem rewards.
The cliff period is a defined duration at the start of the vesting schedule during which no tokens are released. If a recipient exits before the cliff ends, they forfeit all tokens. For example, a 1-year cliff on a 4-year schedule means the recipient receives 0% of their allocation for the first 12 months. Upon reaching the cliff, a significant portion (often 25% in this example) typically becomes claimable all at once. This model is used to ensure a minimum commitment period from the beneficiary before any value is unlocked.
After the cliff, tokens usually vest linearly over the remaining period. Linear vesting releases tokens in a continuous, pro-rata fashion. Using the 4-year schedule with a 1-year cliff, the remaining 75% of tokens would vest linearly over the subsequent 36 months. This means approximately 2.08% of the total allocation becomes claimable each month. The formula is straightforward: Vested Amount = (Total Allocation * (Current Time - Cliff End) / Vesting Duration). This predictable, smooth unlock helps manage market dilution and provides consistent incentives.
For more complex needs, custom vesting schedules offer granular control. These can be implemented by defining discrete milestones or using a piecewise function within the smart contract. Examples include: a stepped schedule (e.g., 10% every 6 months), performance-based unlocks tied to KPIs, or a hybrid model combining an initial cliff with non-linear releases. Custom schedules require more sophisticated contract logic, often using a vesting curve or a series of timestamps and amounts. They are powerful for tailoring incentives to specific agreements or fundraising rounds.
When implementing vesting in a smart contract, you typically manage a structure for each beneficiary containing their totalAllocation, claimedAmount, cliffEnd, and vestingEnd. The core function calculates the vestedAmount at any block timestamp. For a linear schedule post-cliff, the Solidity logic might be: uint256 vested = (total * (time - cliffEnd)) / (vestingEnd - cliffEnd);. It's crucial to use safe math libraries and ensure the contract owner can revoke unvested tokens in case of early termination, as defined in the legal agreement.
Best practices include clearly communicating schedule terms to recipients, using audited vesting contract templates like OpenZeppelin's VestingWallet, and considering multi-signature controls for administrative functions. Always verify that the token contract itself allows the vesting contract to hold and distribute tokens, typically by granting an allowance. For transparency, the schedule parameters should be visible on-chain, allowing anyone to verify unlock timelines. Properly designed vesting is a cornerstone of sustainable tokenomics and governance.
Vesting Schedule Patterns and Use Cases
Comparison of common vesting schedule structures, their mechanics, and ideal applications for token distribution.
| Schedule Type | Cliff Period | Vesting Cadence | Typical Duration | Primary Use Case |
|---|---|---|---|---|
Linear (Straight-line) | 0-12 months | Per second or daily | 2-4 years | Core team & early employees |
Graded Vesting | 3-6 months | Monthly or quarterly | 3-5 years | Advisors & contractors |
Bullet Vesting | Fully at cliff | Single lump sum | 1-3 years | Token sale investors (SAFT/SAFE) |
Step-function | 6-12 months | Quarterly tranches | 4+ years | Founders with long-term alignment |
Performance-based | Variable | Milestone-triggered | Indefinite | CEOs & key hires with KPIs |
Hybrid (Cliff + Linear) | 1 year | Monthly after cliff | 4 years | Standard employee equity packages |
Reverse Vesting | N/A (applies on exit) | Single trigger event | N/A | Founders (protects company on departure) |
Implementation: A Linear Vesting Contract
A step-by-step guide to building a secure, on-chain linear vesting contract for token distribution, team allocations, and investor lockups.
A linear vesting contract is a foundational tool for managing token distributions, ensuring that assets like team allocations, investor tokens, or advisor grants are released gradually over time. This mechanism prevents large, immediate sell-offs that can destabilize a token's price. The core logic is simple: a beneficiary is granted a total allocation (totalAmount) that becomes claimable in a linear fashion, starting from a startTimestamp and ending at a cliffTimestamp. Before the cliff, no tokens are claimable; after the cliff, the claimable amount increases proportionally with each passing second until the full amount is unlocked at the endTimestamp.
The key calculation is the vested amount at any given block timestamp. The formula is: vestedAmount = (totalAmount * (currentTime - startTime)) / (endTime - startTime). This value is capped at totalAmount. The contract must track how much each beneficiary has already withdrawn to prevent double-claiming. A basic implementation involves a mapping like mapping(address => Beneficiary) public beneficiaries, where the Beneficiary struct stores the total allocation, amount claimed, and the vesting schedule parameters. Security checks, like ensuring the current timestamp is past the cliff, are critical.
Here is a simplified core function for calculating the currently claimable amount in Solidity:
solidityfunction vestedAmount(address beneficiary) public view returns (uint256) { Beneficiary memory b = beneficiaries[beneficiary]; if (block.timestamp < b.cliffTimestamp) return 0; if (block.timestamp >= b.endTimestamp) return b.totalAmount; uint256 timeSinceStart = block.timestamp - b.startTimestamp; uint256 vestingDuration = b.endTimestamp - b.startTimestamp; return (b.totalAmount * timeSinceStart) / vestingDuration; }
The claim() function would then calculate the vested amount, subtract what has already been claimed, and transfer the difference to the caller, updating the claimed balance. Always use the Checks-Effects-Interactions pattern and consider reentrancy guards, especially if the token contract is unknown.
For production use, extend this basic contract with essential features. Implement an admin function to add beneficiaries, which should transfer the total vesting amount into the contract upfront or ensure sufficient allowance. Include emergency revocation for cases like a team member leaving, where unvested tokens can be reclaimed by the admin. Consider adding a function for beneficiaries to delegate their vested tokens to a smart contract for voting or staking without claiming. Always verify the contract with a tool like Slither and audit the final code, as vesting contracts often hold significant value.
Testing is non-negotiable. Write comprehensive unit tests (using Foundry or Hardhat) that simulate the full vesting timeline. Key test cases include: claiming zero tokens before the cliff, a partial claim after the cliff, a full claim at the end, and attempting to claim twice. Also test edge cases and admin functions. Once deployed, the contract's immutable schedule provides transparent, trustless enforcement of the vesting terms, a significant improvement over opaque, off-chain agreements. For real-world examples, review established contracts like those from OpenZeppelin's VestingWallet.
Implementation: Multi-Beneficiary Team Vesting
A practical guide to implementing a secure, gas-efficient vesting contract for distributing tokens to multiple team members with customizable schedules.
Multi-beneficiary vesting contracts are essential for managing token distributions to founders, employees, and advisors. Unlike a simple linear release, these contracts allow a single deployment to handle dozens or hundreds of individual schedules, significantly reducing gas costs and administrative overhead. The core logic involves tracking each beneficiary's allocation, cliff period, vesting duration, and amount already claimed. A well-designed contract separates the vesting logic from the token itself, interacting via the ERC-20 transfer function for payouts, which enhances security and auditability.
The contract structure typically includes a beneficiary struct to store key parameters: totalAllocation, claimed, cliffEnd, and vestEnd. A mapping like mapping(address => Beneficiary) public beneficiaries links each wallet to its schedule. The critical function vestedAmount(address beneficiary, uint256 timestamp) calculates the releasable tokens at any given moment using a linear formula: (totalAllocation * (timestamp - cliffEnd)) / (vestEnd - cliffEnd). This function must be view-only and handle edge cases like timestamps before the cliff.
For deployment, you must first approve the vesting contract to spend the total token allocation from the deployer's wallet. Initialization involves calling an addBeneficiary function (often restricted to the owner) for each team member, setting their unique schedule. It's crucial to verify that the sum of all totalAllocation values does not exceed the contract's token balance. Use events like BeneficiaryAdded and TokensReleased for transparent on-chain logging. Always conduct a test deployment on a testnet with simulated time jumps to validate the vesting math.
Security considerations are paramount. The contract should include a beneficiary modifier to restrict the release function, preventing unauthorized withdrawals. Consider implementing an emergency revoke function for the owner, allowing the retrieval of unvested tokens in case a beneficiary leaves the project, but this should have a timelock and multi-sig requirement. Avoid using block.timestamp directly in critical calculations without understanding its manipulability in edge cases; it is generally sufficient for vesting.
To interact with the contract, beneficiaries call the release() function, which calculates the currently vested amount, transfers the claimable tokens, and updates the claimed state. A common front-end pattern fetches the vestedAmount for a user's address to display a progress bar. For teams, using a merkle tree to set initial beneficiaries in a single transaction can further optimize gas, though it makes schedules immutable. Always publish the verified source code on Etherscan and consider an audit for contracts holding significant value.
Tools and Reference Implementations
Practical tools and audited reference implementations for setting up onchain vesting schedules and token lockups. These resources cover both DIY smart contract approaches and production-ready protocols used in live token distributions.
Custom Vesting Contracts with Cliffs and Revocation
When standard tools are insufficient, teams often implement custom vesting contracts tailored to their governance and legal requirements.
Common custom features:
- Cliff periods followed by linear or step-based vesting
- Revocable vesting for employees or contractors
- Integration with DAO governors or multisigs for revocation authority
- Emergency pause and upgrade paths via proxies
Best practices:
- Base implementations on audited primitives like OpenZeppelin
- Avoid block-number based schedules; always use timestamps
- Hardcode immutable vesting parameters wherever possible
- Include comprehensive unit tests around edge cases
Custom contracts increase flexibility but also increase audit scope and risk, so they should only be used when off-the-shelf primitives cannot meet requirements.
Security Risks and Mitigations
Comparison of security models for managing token vesting and lockups.
| Risk Category | Centralized Custody | Multi-Sig Admin | Fully On-Chain / DAO |
|---|---|---|---|
Admin Key Compromise | Complete fund loss | Requires M-of-N compromise | Immutable rules; no admin |
Upgradeability / Pause | Central admin can halt or alter | M-of-N can upgrade or pause | No pause; upgrades via governance |
Censorship Risk | High - Single entity can block | Medium - Requires collusion | Low - Rules are permissionless |
Transparency | Low - Opaque backend | Medium - On-chain txns visible | High - All logic is verifiable |
Gas Cost for Users | Low (off-chain computation) | Medium | High (on-chain execution) |
Recovery from Bug | Fast via admin action | Slow, requires multi-sig coordination | Very slow; requires governance fork |
Example Protocol | CoinList Custody | Sablier w/ Gnosis Safe | Vesting contract on Uniswap DAO |
How to Set Up Vesting and Lockups
A practical guide to implementing secure, gas-efficient token vesting and lockup schedules using Solidity, with a focus on testing strategies and deployment best practices.
Token vesting and lockups are critical mechanisms for aligning long-term incentives in Web3 projects. A vesting schedule releases tokens to beneficiaries (e.g., team members, investors, advisors) linearly over time, while a lockup prevents the transfer of tokens entirely for a fixed period. These contracts manage the distribution of potentially large token allocations, making their security and correctness paramount. Common implementations include cliff periods (an initial duration with no unlocks), linear vesting, and milestone-based releases. The primary security risk is flawed logic that allows premature or incorrect withdrawals, which can undermine project stability and trust.
For robust implementation, use a battle-tested, audited codebase as a starting point. The OpenZeppelin Contracts library provides a secure VestingWallet base contract. A typical setup involves creating a contract that inherits from VestingWallet or a similar pattern, defining the beneficiary address, start timestamp, and duration. For more complex schedules (e.g., with a cliff), you extend the base logic. Here's a minimal example:
solidityimport "@openzeppelin/contracts/finance/VestingWallet.sol"; contract TeamVesting is VestingWallet { constructor(address beneficiary, uint64 startTimestamp, uint64 durationSeconds) VestingWallet(beneficiary, startTimestamp, durationSeconds) {} }
Deploy this contract and fund it with the total vested token amount. The beneficiary can call release(address token) to claim their vested amount at any time.
Comprehensive testing is non-negotiable. Your test suite should simulate the full timeline of the vesting schedule. Key test scenarios include: verifying that zero tokens are releasable before the cliff, calculating the correct vested amount at any given block timestamp, ensuring only the beneficiary can release funds, and testing the full release at the end of the schedule. Use Foundry or Hardhat to warp block timestamps and test time-dependent logic. For example, in Foundry, you can use vm.warp(cliffEndTimestamp + 1) to simulate time passage. Always test edge cases like deploying with a start time in the past or a zero-duration schedule.
Before mainnet deployment, execute a formal checklist. 1. Audit & Review: Get a professional audit or extensive peer review. 2. Parameter Verification: Double-check all constructor arguments (beneficiary addresses, start times, durations) – mistakes are irreversible. 3. Dry-Run on Testnet: Deploy to a testnet like Sepolia or Goerli and simulate the entire vesting lifecycle. 4. Ownership & Admin: Confirm the contract has no unnecessary admin functions that could halt vesting; the schedule should be immutable. 5. Token Compatibility: Ensure your vesting contract works with the specific ERC-20 token (including potential rebasing or fee-on-transfer tokens). 6. Emergency Considerations: While schedules should be immutable, some projects implement a multi-sig sweep function for retrieving erroneously sent tokens other than the vested asset.
For advanced patterns, consider batch vesting contracts (single contract managing multiple beneficiaries for gas efficiency) and revocable vesting (where tokens can be clawed back under specific conditions, requiring careful legal and technical design). Always document the vesting schedule clearly for beneficiaries, potentially emitting events for each release or schedule change. By methodically implementing, testing, and deploying vesting contracts, you create a transparent and trust-minimized foundation for long-term stakeholder alignment.
Frequently Asked Questions
Common technical questions and solutions for implementing and managing token vesting schedules and lockups using smart contracts.
A lockup is a period where tokens are completely frozen and cannot be transferred, sold, or staked. It's a simple time-based restriction. A vesting schedule is a mechanism that gradually releases tokens to beneficiaries over time (e.g., monthly or quarterly), often after an initial cliff period.
- Lockup Example: A team's tokens are locked for 1 year, then released all at once.
- Vesting Example: A 4-year vesting schedule with a 1-year cliff (0% released before 1 year), then 25% released, followed by linear monthly releases for the remaining 3 years.
Vesting is built on top of a lockup; the cliff is a lockup, and the linear release is the vesting schedule. Smart contracts like OpenZeppelin's VestingWallet or custom implementations manage these rules programmatically.
Conclusion and Next Steps
You have now implemented a foundational vesting and lockup system. This guide covered the core concepts, smart contract structure, and deployment process.
The vesting contract you built uses a linear release schedule, a common and predictable model. Key security features you implemented include: - Immutable beneficiary: The recipient address is set at deployment. - Owner-only cliff adjustment: Only the contract owner can modify the cliff period before vesting starts. - Reentrancy guards: The claim function is protected against reentrancy attacks. Remember, this is a basic template. For production use, you must conduct a thorough audit and consider additional features like multi-sig ownership, transferability of vested tokens, or support for ERC-20 token approvals.
To extend this system, consider integrating with popular development frameworks. For Foundry, you can write comprehensive tests for edge cases like early claims and owner functions. For Hardhat, you can create a deployment script that interacts with a frontend dashboard. A common next step is to build a simple React dApp interface that connects via Ethers.js or Viem, allowing beneficiaries to view their vested balance and claim tokens directly from a web page. You can find starter templates for such dashboards on GitHub repositories like OpenZeppelin Contracts Wizard.
For more complex requirements, explore established solutions in the ecosystem. The OpenZeppelin VestingWallet contract is a robust, audited base contract you can inherit from. Protocols like Sablier and Superfluid offer sophisticated, real-time streaming of tokens, which is a more granular form of vesting. Your choice depends on the need for customization versus the security of a battle-tested protocol.
Always prioritize security in financial smart contracts. Before deploying any vesting contract with real value, take these steps: 1) Test extensively on a testnet like Sepolia or Holesky. 2) Get an audit from a reputable firm if the total value locked (TVL) is significant. 3) Implement emergency stops or multi-sig controls for the owner role to mitigate unforeseen issues. The code you write today manages real assets and trust.