Skip to main content
A gas-optimized, multi-token vesting contract for linear vesting schedules with optional cliffs.

Overview

MultiTokenVesting is a Solidity smart contract designed for linear vesting of multiple ERC-20 tokens from a single deployment. An Owner (admin) creates vesting schedules for beneficiaries, and beneficiaries claim vested tokens over time. Key behaviors:
  • Vesting is linear (optionally with a cliff)
  • Schedules can be revoked by the Owner
  • The contract enforces a solvency check by pulling tokens in at schedule creation

Key features

Multi-token support

One contract can manage vesting schedules for many ERC-20 tokens (e.g., USDC, WETH, UNI), without redeploying per token.

Revocable schedules

When the Owner revokes a schedule:
  • The vested amount remains claimable by the beneficiary
  • The unvested portion is refunded back to the Owner

Gas optimization

The contract is optimized for production use through:
  • Packed data structures (addresses + flags)
  • Custom errors (cheaper than revert strings)
  • Direct claiming by schedule index

Safety features

  • Solvency check: tokens required for a schedule are transferred into the contract at creation time
  • Withdraw excess: the Owner can withdraw only excess tokens accidentally sent to the contract (not those locked for active schedules)

Supported tokens

  • ERC-20 tokens are supported.
Native gas tokens (e.g., using PEAQ as a native currency) are not supported directly by this contract. Wrap native tokens into an ERC-20 representation if needed.

Vesting logic

Vesting follows a simple timeline:
  • Before cliff: 0 claimable
  • After cliff → end: linear unlock
  • After end: 100% claimable
Formula: VestedAmount = (TotalAmount * (CurrentTime - StartTime)) / Duration Example:
  • Total amount: 1000
  • Duration: 1000 seconds
  • Cliff: 250 seconds
Progression:
  • At 0s: 0 vested
  • At 250s: 250 vested
  • At 500s: 500 vested
  • At 1000s: 1000 vested

Quickstart (peaq-friendly)

You deploy MultiTokenVesting on peaq EVM like any Solidity contract: If you prefer Foundry, these commands match common peaq setups:
forge install OpenZeppelin/openzeppelin-contracts --no-commit
forge build
forge test -vv

How to use

A) Approve tokens (ERC-20)

Approve the vesting contract to pull tokens from the Owner’s wallet. This call is made on the ERC-20 token contract, not the vesting contract.
IERC20(tokenAddress).approve(vestingContractAddress, amount);

B) Create vesting schedule (Owner-only)

Create a schedule that starts now, has a 30-day cliff, and vests linearly over 1 year.
vesting.createVestingSchedule(
  beneficiary,
  token,
  amount,
  block.timestamp,
  2592000,   // 30 days
  31536000   // 1 year
);

C) Claim (Beneficiary-only)

Beneficiaries claim by scheduleIndex:
vesting.claim(scheduleIndex);
The scheduleIndex is emitted by the ScheduleCreated event. In practice, your app should index this event and store schedule indices per beneficiary.

D) Revoke (Owner-only)

The Owner can revoke a schedule:
vesting.revoke(scheduleIndex);
On revoke:
  • Beneficiary keeps vested amount up to the revoke time
  • Remaining unvested amount returns to the Owner

E) Withdraw excess (Owner-only)

Withdraw tokens that are not locked in active schedules:
vesting.withdrawExcess(tokenAddress);

API reference

VestingSchedule fields

  • beneficiary (address)
  • start (uint64)
  • revoked (bool)
  • claimed (bool)
  • token (address)
  • duration (uint64)
  • cliff (uint64)
  • totalAmount (uint256)
  • amountClaimed (uint256)

Errors

ErrorMeaning
InvalidAddressAn address argument is zero or invalid
InvalidAmountAmount is zero or invalid
InvalidDurationDuration is zero or invalid
InvalidCliffCliff is invalid (e.g., greater than duration)
UnauthorizedCaller is not allowed to perform this action
ScheduleClaimedSchedule was already fully claimed
NothingToClaimNo tokens are currently claimable
InvalidIndexProvided schedule index does not exist
ScheduleWasRevokedSchedule was revoked and no longer claimable in the expected way
InsufficientExcessBalanceNot enough excess balance to withdraw

Security notes

  • Solidity ^0.8.20 includes overflow checks by default.
  • Uses SafeERC20 to safely handle non-standard ERC-20 implementations.
  • Claim flows should follow Checks-Effects-Interactions.
  • Centralization risk: the Owner can revoke schedules (beneficiaries must trust the Owner).

References