Module 09: Vesting

Client: client.vesting Weight: Light

Prerequisites

Next steps after vesting

  • Need liquidity before unlock? Take a vesting loan (§d below)
  • Launching with a distribution plan? Combine with token creation
  • Distributing to many recipients? Use batch creation (§a)

Why Vesting Matters

Vesting locks tokens and releases them on a schedule you define. Use it to:

  • Lock team/advisor/investor tokens to signal long-term commitment
  • Distribute allocations that release over months or years
  • Build trust with your community — locked tokens can't be dumped immediately
  • Access liquidity before unlock by taking a loan against a vesting position

How It Works

Two vesting types:

TypeBehavior
CliffAll tokens unlock at a single point in time (minimum 1 hour from now)
GradualTokens stream linearly over a duration, unlocking at each time unit interval

Roles:

  • Creator — creates the schedule, adds tokens, changes beneficiary, extends duration, transfers creator role. Must call from creator wallet.
  • Beneficiary — claims unlocked tokens, takes/repays vesting loans. Must call from beneficiary wallet.

These roles are strictly separate. Wrong caller reverts.

Active loans block modifications. If a vesting position has an active loan, you cannot modify it until the loan is repaid.

No price liquidation on vesting loans — expiry is time-based only, same as regular loans.


Actions

a. Create Vesting

createGradualVesting(beneficiary, token, totalAmount, startTime, durationInDays, timeUnit, memo, ecosystem)

Creates a linear vesting schedule. Tokens unlock progressively at each timeUnit interval throughout the duration.

Critical: Use now() + 60 for startTime. Using now() will be in the past by the time the transaction confirms.

// JS
const result = await client.vesting.createGradualVesting(
  "0xBeneficiary",
  "0xToken",
  parseUnits("10000", 18),
  Math.floor(Date.now() / 1000) + 60,  // now + 60 seconds
  365,   // duration in days
  3,     // timeUnit: 3 = daily unlocks
  "Team allocation",
  MAINTOKEN
);
# Python
import time
result = client.vesting.create_gradual_vesting(
    "0xBeneficiary", "0xToken", 10000 * 10**18,
    int(time.time()) + 60, 365, 3, "Team allocation", MAINTOKEN
)
ParamTypeNotes
beneficiarystringRecipient address
tokenstringToken contract to vest
totalAmountbigint/intTotal tokens (18 decimals)
startTimebigint/intUnix timestamp — use now() + 60, never now()
durationInDaysbigint/intTotal vesting duration, always expressed in days
timeUnitnumberUnlock frequency: 0=seconds, 1=minutes, 2=hours, 3=days
memostringOptional label
ecosystemstringMAINTOKEN address

timeUnit examples: durationInDays=30, timeUnit=2 = hourly unlocks over 30 days (720 events). durationInDays=30, timeUnit=3 = daily unlocks over 30 days (30 events).


createCliffVesting(beneficiary, token, totalAmount, unlockTime, memo, ecosystem)

All tokens unlock at unlockTime in a single event. Minimum unlockTime is 1 hour from now — anything less reverts.

ParamTypeNotes
beneficiarystringRecipient address
tokenstringToken contract to vest
totalAmountbigint/intTotal tokens (18 decimals)
unlockTimebigint/intUnix timestamp, minimum now + 3600
memostringOptional label
ecosystemstringMAINTOKEN address

Batch Creation

MethodDescription
batchCreateGradualVesting(...)Multiple gradual schedules in one transaction. Same params as singular, passed as arrays.
batchCreateCliffVesting(...)Multiple cliff schedules in one transaction.

Batch creation is the efficient path when distributing to many recipients at once (e.g., investor round, team allocation).


b. Claim Tokens

claimTokens(vestingId)

Claims all currently unlocked tokens. Only the beneficiary can call this.

  • For cliff vesting: only callable after unlockTime
  • For gradual vesting: callable anytime after vesting starts, claims whatever has vested since last claim
  • Check getClaimableAmount(vestingId) before calling to confirm non-zero amount
const claimable = await client.vesting.getClaimableAmount(vestingId);
if (claimable > 0n) {
  await client.vesting.claimTokens(vestingId);
}

c. Manage Vesting (Creator Only)

All methods below require the caller to be the creator of the vesting schedule.

MethodParamsNotes
changeBeneficiary(vestingId, newBeneficiary)vestingId, addressTransfers recipient role to new address
extendVestingPeriod(vestingId, additionalDays)vestingId, numberAdds days to the vesting duration; fails if vesting already ended
addTokensToVesting(vestingId, additionalAmount)vestingId, bigintAdds tokens to the schedule; auto-approves
transferCreatorRole(vestingId, newCreator)vestingId, addressHands off creator control

Blocked by active loans. Repay the vesting loan first before modifying the schedule.


d. Vesting Loans (Beneficiary Only)

Borrow against a vesting position to access liquidity before tokens unlock.

takeLoanOnVesting(vestingId, amount, days)

Takes a USDB loan against the vesting position. Same mechanics as regular BASIS loans:

ParamTypeDescription
vestingIdbigint/intVesting schedule ID
amountbigint/intCollateral amount from the vesting (18 decimals)
daysbigint/intLoan duration in days (minimum: 10, maximum: 1000)
  • Origination fee: 2% flat
  • Interest: 0.005%/day
  • Duration: 10–1000 days
  • Expiry: time-based only — no price liquidation

repayLoanOnVesting(vestingId)

Repays the active loan on the vesting position. Auto-approves USDB.

Only one active loan per vesting position at a time. Check getActiveLoan(vestingId) to see current loan ID.


Read Methods

MethodReturnsDescription
getVestingDetails(vestingId)Vesting structFull schedule details: parties, token, amounts, timing, loan status
getClaimableAmount(vestingId)bigintTokens available to claim right now
getVestedAmount(vestingId)bigintTotal tokens vested so far (including already claimed)
getVestingsByBeneficiary(address)bigint[]All vesting IDs where address is beneficiary
getVestingsByCreator(address)bigint[]All vesting IDs created by address
getActiveLoan(vestingId)bigintActive loan ID, or 0 if none
getTokenVestingIds(token, startIndex, endIndex)bigint[]Vesting IDs for a token within index range
getVestingDetailsBatch(vestingIds[])Vesting[]Details for multiple schedules in one call
getVestingCount()bigintTotal vesting schedules ever created

getVestingDetails struct fields:

FieldTypeDescription
creatoraddressWho created the schedule
beneficiaryaddressWho receives tokens
tokenaddressVested token contract
totalAmountuint256Total tokens in schedule
claimedAmountuint256Tokens already claimed
startTimeuint256Unix timestamp when vesting begins
durationInDaysuint256Gradual duration (0 for cliff)
unlockTimeuint256Cliff unlock timestamp (0 for gradual)
isGradualbooltrue = gradual, false = cliff
activeLoanIduint256Active loan ID, 0 if none
memostringLabel set at creation
timeUnituint80=seconds, 1=minutes, 2=hours, 3=days

Fees

ActionFee
Create vesting (any type)None
Vesting loan origination2% of loan amount
Vesting loan interest0.005% per day

Errors

ErrorWhenFix
"Vesting does not exist"Any operation on invalid IDVerify via getVestingsByBeneficiary() or getVestingsByCreator()
"Only beneficiary can claim/repay/take loan"Wrong callerCall from the beneficiary wallet
"Only creator can add tokens/change beneficiary"Wrong callerCall from the creator wallet
"No unclaimed tokens"claimTokens()Still in cliff period or all tokens claimed; check getClaimableAmount()
"Vesting already ended"extendVestingPeriod()Can't extend a completed schedule; create a new one
"Vesting duration must be at least 1 hour"createVesting()Duration or unlockTime must be at least 3600 seconds from now
"Start time must not be in the past"createGradualVesting()Use a future timestamp; now() + 60 is the safe minimum
"Active loan exists"Modifying vestingRepay the vesting loan first, then modify

What Next

  • Monitor claims: Poll getClaimableAmount() periodically for beneficiaries to know when to claim
  • Loan against positions: Use takeLoanOnVesting() for liquidity before unlock
  • Trust signal: Publicly visible locked tokens signal commitment; use alongside token launches
  • Batch distribution: Use batchCreateGradualVesting() or batchCreateCliffVesting() for investor rounds to save gas

See Also