Vesting
Linear vesting with a cliff, for any ERC-20, custodied by one contract and claimed by the recipient. Three variants exist for three different situations.
Why this exists
A memecoin team, a KOL deal, or an advisor allocation all have the same shape: tokens that should be spendable later, not now, with a schedule everyone can verify. Announcing a lockup in a Telegram post is not a lockup. This is.
The core contract
One contract custodies many independent schedules keyed by a numeric id. It is not a factory and there is no per-recipient deployment.
0
1
function createVesting(
address token,
address beneficiary,
uint256 amount,
uint64 start,
uint64 cliff,
uint64 duration,
bool revocable
) external payable returns (uint256 id);
2
function release(uint256 id) external;
3
function revoke(uint256 id) external;
function releasable(uint256 id) external view returns (uint256);
function getSchedule(uint256 id) external view returns (
address creator, address token, address beneficiary,
uint256 amount, uint256 released,
uint64 start, uint64 cliff, uint64 duration,
bool revocable, bool revoked
);KOL vesting on Robinhood Chain: 0xcF51e2c48e97e52cE9fF51a58876aCf12E455b42
KOL vesting on BNB Chain: 0x9b7c11b6e8848cd1c865381f580365d1256bd3ba
How a schedule behaves
- Before the cliffNothing is releasable.
releasable(id)returns zero. - At the cliffEverything accrued between
startand now becomes releasable at once. - Between cliff and endLinear accrual. The recipient can claim as often as they like; each claim pays what has accrued since the last one.
- After
start + durationThe whole amount is releasable.
release(id) can be called by anyone and the tokens always go to the schedule's beneficiary. A recipient who cannot afford gas can have someone else trigger their claim without any trust involved.
The three variants
| Variant | What the recipient receives | Fee |
|---|---|---|
| KOL vesting | The vested token itself | 5% in gas token, paid at creation |
| Claim-swap | The vested token, swapped — free to create | 2.5% in gas token, taken per claim |
| Deposit / daily cap | Tokens, released against a dollar-per-day cap priced by a keeper | Per configuration |
KOL vesting
The plain version. Pay the creation fee once, and every claim afterwards is free. Best when the recipient will claim many times.
Claim-swap
Free to create; the fee is taken on each claim instead. Best when you are setting up many schedules and want no up-front cost, or when the number of claims will be small.
Daily-cap vesting
Releases are limited to a dollar amount per day rather than a token amount, with the price supplied by a keeper reading a price feed with an on-chain pool fallback. Useful when the point of the lockup is to limit sell pressure in dollar terms rather than in units.
The "pay tokens" configuration pays out the vested token itself and performs no swap. Do not read a daily cap as a promise that tokens are being sold — it caps the release, not the market impact.
Revocation
A schedule created with revocable = true can be cancelled by its creator. Revoking pays out everything already accrued to the beneficiary and refunds the still-locked remainder to the creator. A schedule created with revocable = false can never be cancelled by anyone.
It is a constructor argument on the schedule and cannot be changed afterwards. A recipient accepting a deal should check this field: a revocable schedule is a promise the creator can withdraw.
Creating a schedule
- Approve the vesting contractThe contract pulls tokens with
safeTransferFrom, so an allowance must exist first. - Set the termsToken, beneficiary, amount, start, cliff, duration, revocable.
- Send with the feeThe only on-chain gate is that
msg.valuemeets the contract's minimum. The percentage is a frontend valuation.
Recipients: finding your schedules
The dashboard reads schedules entirely from chain — no API, no keeper, no backend. It pulls VestingCreated logs from the contract's deploy block and filters for your address locally, then reads getSchedule and releasable for each id.
The beneficiary is in the log's data, not in an indexed topic, so the logs cannot be filtered server-side by recipient — they must all be pulled from the deploy block and filtered client-side. Get the fromBlock and the local filter right and a schedule always shows. There is no backend that can be down.
What to check before accepting a vesting deal
- Is it revocable?
getSchedule(id).revocable. - Is it funded? The tokens are pulled at creation, so a schedule that exists is a schedule that holds its tokens. Verify the amount.
- What is the cliff? Nothing is releasable before it, no matter how long the schedule has been running.
- Which token? A schedule on a worthless token is a worthless schedule, however well drafted.