Token Vesting

Linear token vesting with cliff periods and revocable schedules

Pending Anchor 0.32
Program ID 9e9VNYBpSGHbF2xbhGJyZDp33WGUQ6S1kpjDxoQXijbp
Binary Size 298K
Cluster Devnet

Instructions

IX create_vesting

Creates a new vesting schedule and deposits tokens into the vault. Tokens unlock linearly between cliff and end timestamps.

ParameterTypeDescription
total_amountu64Total number of tokens to vest
start_tsi64Unix timestamp when vesting begins
cliff_tsi64Unix timestamp of the cliff (no claims before this)
end_tsi64Unix timestamp when all tokens are fully vested
revocableboolWhether the authority can revoke the schedule
IX claim

Claims all currently vested tokens that have not yet been withdrawn by the beneficiary.

ParameterTypeDescription
No parameters
IX revoke

Revokes a vesting schedule and returns unvested tokens to the authority. Only works if revocable was set to true.

ParameterTypeDescription
No parameters

Accounts

VestingSchedule

FieldTypeDescription
authorityPubkeyWallet that created and controls the vesting schedule
beneficiaryPubkeyWallet that receives the vested tokens
mintPubkeyToken mint being vested
total_amountu64Total tokens in the vesting schedule
claimed_amountu64Tokens already claimed by the beneficiary
start_tsi64Vesting start timestamp
cliff_tsi64Cliff timestamp (no claims before this)
end_tsi64Timestamp when vesting is fully complete
revocableboolWhether the schedule can be revoked
revokedboolWhether the schedule has been revoked
bumpu8PDA bump seed

PDA Derivation

vesting
seeds = [b"vesting", authority, mint]
vault
seeds = [b"vault", vesting]

Error Codes

CliffNotReached Cannot claim tokens before the cliff timestamp
NothingToClaim No additional tokens have vested since the last claim
AlreadyRevoked The vesting schedule has already been revoked
NotRevocable This vesting schedule was created as non-revocable
InvalidSchedule The provided timestamps are invalid (start must be before cliff, cliff before end)
Overflow Arithmetic overflow in vested amount calculation

Usage Example

import { Program, BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";

const now = Math.floor(Date.now() / 1000);

// Create a 12-month vesting schedule with a 3-month cliff
await program.methods
  .createVesting(
    new BN(10_000_000),           // total_amount
    new BN(now),                   // start_ts
    new BN(now + 90 * 86400),     // cliff_ts (3 months)
    new BN(now + 365 * 86400),    // end_ts (12 months)
    true                           // revocable
  )
  .accounts({
    authority: authority.publicKey,
    beneficiary: beneficiary.publicKey,
    mint,
  })
  .signers([authority])
  .rpc();