> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peaq.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# MultiTokenVesting

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.

<Note>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.</Note>

## 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:

* Build and understand the contract: [Build Smart Contract](/peaqchain/build/basic-operations/smart-contracts/build-smart-contract)
* Deploy it: [Deploy Smart Contract](/peaqchain/build/basic-operations/smart-contracts/deploy-smart-contract)
* Interact with it: [Interact with Smart Contract](/peaqchain/build/basic-operations/smart-contracts/interact-with-smart-contract)

If you prefer Foundry, these commands match common peaq setups:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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.

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
vesting.createVestingSchedule(
  beneficiary,
  token,
  amount,
  block.timestamp,
  2592000,   // 30 days
  31536000   // 1 year
);
```

### C) Claim (Beneficiary-only)

Beneficiaries claim by `scheduleIndex`:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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:

```solidity theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
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

| Error                       | Meaning                                                          |
| --------------------------- | ---------------------------------------------------------------- |
| `InvalidAddress`            | An address argument is zero or invalid                           |
| `InvalidAmount`             | Amount is zero or invalid                                        |
| `InvalidDuration`           | Duration is zero or invalid                                      |
| `InvalidCliff`              | Cliff is invalid (e.g., greater than duration)                   |
| `Unauthorized`              | Caller is not allowed to perform this action                     |
| `ScheduleClaimed`           | Schedule was already fully claimed                               |
| `NothingToClaim`            | No tokens are currently claimable                                |
| `InvalidIndex`              | Provided schedule index does not exist                           |
| `ScheduleWasRevoked`        | Schedule was revoked and no longer claimable in the expected way |
| `InsufficientExcessBalance` | Not 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).
