# Introduction

TokenTable, developed by ***Sign***, is a suite of on chain token distribution products designed to streamline token ownership registration and distribution through smart contracts.

### TokenTable Standard

**TokenTable Standard Spreadsheet** is a free and powerful tool designed to help founders clearly and succinctly communicate their tokenomics to the community, investors, and future exchange partners. It is developed from direct, candid conversations with exchanges about what they actually need to see, combined with proven practices for building sustainable token economies.

Make a copy of the spreadsheet [here](https://docs.google.com/spreadsheets/d/1blxrXi8WbhDw8h31y_BBYz15bDeiZ36_XAAjWMKz-5E/edit?usp=sharing).

### TokenTable Airdrop

TokenTable Airdrop is designed for large-scale token airdrops, efficiently handling token claims for over 40 million users. It is compatible with all EVM networks, TON, and Solana. Recently, we have implemented a signature-based airdrop solution, **making token distribution possible with Web2 credentials** such as X handle and Telegram account. Some notable projects include:

* **KAITO**: Airdropped **$30M** to **150K users**, verifying via X handles
* **DOGS**: Airdropped **$130M+** to **30M+ users**
* **ZetaChain**: Distributed **$12M** to **200K users** across its ecosystem

Please [contact us](mailto:inquiries@ethsign.xyz) for more details on how to get started with an Airdrop project.

### TokenTable Unlocker

TokenTable Unlocker is designed for more fine-tuned token unlocking for a smaller group of recipients, such as investor token unlocks and treasury management. It offers unique advantages such as unruggability and complete decentralization. Past projects include:

* **Virtuals:** Facilitated genesis launch for all projects on launchpad
* **StarkNet**: Unlocked **$40M** for investors
* **DOGS**: Unlocked **$29M** for investor allocations

Please [contact us](mailto:inquiries@ethsign.xyz) for more details on how to get started with an Unlocker project.

### TokenTable Lite

TokenTable Lite is a permissionless, community-first toolset for effortless token distribution. With simple UX and no onboarding barriers, it’s perfect for memecoins, AI agents, fan communities, and social tokens.

Try TokenTable Lite [here](https://app.tokentable.xyz).


# TokenTable Standard

**TokenTable Standard Spreadsheet** is a free and powerful tool designed to help founders clearly and succinctly communicate their tokenomics to the community, investors, and future exchange partners. It is developed from direct, candid conversations with exchanges about what they actually need to see, combined with proven practices for building sustainable token economies.

Make a copy of the spreadsheet [here](https://docs.google.com/spreadsheets/d/1blxrXi8WbhDw8h31y_BBYz15bDeiZ36_XAAjWMKz-5E/edit?usp=sharing) and see below for a detailed breakdown of each section.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-43bb21fa7a0dcbecfcf90d3205bb69fb0278fa38%2Ftokentable-standard.png?alt=media" alt=""><figcaption><p>TokenTable Standard Spreadsheet</p></figcaption></figure>

### Section I: Project Details

Cells **C4-C11** contains details about the token’s core attributes. **Blue text fields require user input, while black text fields populate based on the information provided.** The **Cir. Supply at TGE (%)** will be determined once vesting details are entered later.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-e399676ff02bb08fda9df2e15ee726924d44ff6a%2Ftoken-information.png?alt=media" alt=""><figcaption></figcaption></figure>

### Section II: Token Allocation

Cells **A15–G26** define each token allocation group with the following fields:

* **Pocket Label**: Name of the allocation group.
* **Category**: Classification of the group (some categories have constraints).
* **Percentage**: Share of the total token supply.
* **Total Amount**: Auto-calculated based on **Percentage** and **Max Supply** (cell C10).
* **Wallet Control**: Method of distribution (e.g., Smart Contract, Multi-Sig, Custodial Services).
* **Init. Wallet Address**: On-chain wallet address for initial token distribution at TGE.
* **Valuation**: For investor groups, the company’s valuation at the time of investment.

Once completed, the checker in **rows 29–30** will validate the allocation and provide suggestions (e.g., “Team and investor allocations should not exceed 40%,” “Liquidity should not exceed 3%”).

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-1217f2b8e66430d578dc046da04777feabcd8db9%2Ftoken-allocation.png?alt=media" alt=""><figcaption></figcaption></figure>

The pie chart on the right visualizes token distribution by category and updates in real time as data is entered.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-47d3846e88bb51c0293e55a0e1f8f476147bcc4c%2Ftoken-allocation-graph.png?alt=media" alt=""><figcaption></figcaption></figure>

### Section III: Token Release Schedule

Rows **34–37** capture vesting details for each allocation group:

* **Unlocked at TGE**: % of tokens released on the day of TGE
* **Lockup Duration**: Months tokens remain fully locked post-TGE (cliff period)
* **Unlock Duration**: Months over which tokens are gradually released after the cliff
* **Unlock Frequency**: How often tokens are released (e.g., monthly, quarterly)

Rows **38–123** display monthly token releases, dynamically calculated based on inputs in rows **34–37**. As with allocations, the checker in **rows 29–30** will review the vesting setup and provide suggestions (e.g., “Investor lockup and unlock duration should total at least 36 months”).

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-0d9711a035eae415182919813dfd9992409b086d%2Ftoken-release-schedule.png?alt=media" alt=""><figcaption></figcaption></figure>

The stacked area chart on the right updates in real time to reflect the vesting schedule.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-de4f2adf325594c185d1aba74badbf497220d2f7%2Ftoken-release-stacked-graph.png?alt=media" alt=""><figcaption></figcaption></figure>

Have feedback about the TokenTable Standard Spreadsheet or questions about our other products? Please [let us know](https://docs.google.com/forms/d/e/1FAIpQLSfP2zAyh2kUyhYFRcbo7eQpeM8xzW3Bd4DTzEWDuavP4mfdsg/viewform).


# TokenTable Airdrop

TokenTable Airdrop is compatible with all EVM networks, TON, and Solana. It sets itself apart as the industry-leading token airdrop solution with the following characteristics:

### Large-Scale

Airdrop Pro is capable of handling token distribution to tens of millions of addresses.

### Reputable

Our product is used and trusted by famous projects such as Kaito, DOGS, and ZetaChain. Quality of service is guaranteed.

### Safe

We publish our project smart contracts onchain and use merkle proofs for wallet address verification, ensuring trustless and cost-effective token distribution.

### Expedited

With our simple setup process and responsive team, you may get a project set up and ready to go as late as one day before TGE.

### Flexible

We offer a wide range of claiming schedules (cliffs, daily, monthly, linear, and more) to suit the needs of your project.

### Identity Verification Support

Airdrop Pro allows projects to customize prerequisites before token claims, such as onchain identity verification, KYC, and more.

### Eligibility Verification via Web2 Credentials

Our signature-based technology allows users to verify airdrop eligibility using Web2 identifiers such as X handles, Telegram usernames, and Discord accounts.

### Multi-Chain

You may use Airdrop Pro on all EVM networks, TON, and Solana. We are also open to providing customized support for projects.

[Contact us](mailto:inquiries@ethsign.xyz) now to set up your own Airdrop Pro project.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-889e494487baf27ad0c8bc65e35c5256646b2c90%2Fairdrop-pro.png?alt=media" alt=""><figcaption><p>TokenTable Airdrop Pro</p></figcaption></figure>


# TokenTable Unlocker

TokenTable Unlocker is currently available on Ethereum, Solana, Starknet, Base, Berachain, BNB, Cyber, Scroll, zkSync Era, and Zetachain. Please reach out to us if you would like customized support for another network.

Targeted specifically for fine-tuned and complex token unlocking, Unlocker sets itself apart with the following features:

### Customizable Unlocking Schedules

Users can create immediate releases, linear unlocks with varying frequencies, and flexible pauses. Multiple schedules with different start times are also possible.

### Partial Deposits

Users can deposit and withdraw tokens as needed, without requiring full deposit upfront.

### Unruggable Standard

If desired, users can specify that token distributions are final and can't be canceled or withdrawn, securing recipients’ funds.

### Claiming Tokens on Behalf of Recipients

In situations where recipients cannot claim released tokens themselves, Unlocker lets users send unlocked tokens directly to recipients' wallets.

### Gas Efficiency

Unlocker consolidates all unlocking schedules into one contract for a single blockchain deployment.

### Comprehensive Dashboard

On Unlocker’s comprehensive dashboard, users can easily view and track all relevant information for their projects.

### \[Coming Soon] FutureToken

Users may trade their currently locked tokens as FutureToken NFTs. Stay tuned for more details!

[Contact us](mailto:inquiries@ethsign.xyz) now to set up your Unlocker project.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-eb230821878e4d2075515661a18631e01ac04e99%2Funlocker-main.png?alt=media" alt=""><figcaption><p>TokenTable Unlocker</p></figcaption></figure>


# Audits

TokenTable products have undergone comprehensive smart contract audits to guarantee security.

### TokenTable Airdrop

* Aidrop on EVM chains audit by CODESPECT ([Report 1](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Merkle%20Airdrop%20EVM%20Audit%201%20-%20CODESPECT.pdf), [Report 2](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Monorepo%20EVM%20Audit%201%20-%20CODESPECT.pdf))
* Airdrop on Solana audit by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Merkle%20Airdrop%20Solana%20Audit%201%20-%20CODESPECT.pdf)
* Airdrop on TON audit by [TonTech](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Merkle%20Airdrop%20TON%20Audit%201%20-%20TonTech.pdf)
* Airdrop on Movement audit by [OtterSec](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Merkle%20Airdrop%20Movement%20Audit%201%20-%20OtterSec.pdf)
* Signature-Based Airdrop on EVM chains audit by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Signature%20Airdrop%20EVM%20Audit%201%20-%20CODESPECT.pdf)
* Signature-Based Airdrop on Solana by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Signature%20Airdrop%20Solana%20Audit%200%20-%20CODESPECT.pdf)
* NFT-Gated Airdrop on EVM chains audit by [Nethermind](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Merkle%20Airdrop%20\(NFT%20Gated\)%20EVM%20Audit%201%20-%20Nethermind.pdf)

### TokenTable Unlocker

* Unlocker Cairo audit by Nethermind ([Report 1](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Unlocker%20Cairo%20Audit%201%20-%20Nethermind.pdf), [Report 2](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Unlocker%20Cairo%20Audit%202%20-%20Nethermind.pdf))
* Unlocker on EVM chains audit by [OtterSec](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Unlocker%20EVM%20Audit%201%20-%20OtterSec.pdf) and CODESPECT ([Report 1](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Unlocker%20EVM%20Audit%202%20-%20CODESPECT.pdf), [Report 2](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Monorepo%20EVM%20Audit%201%20-%20CODESPECT.pdf))
* Unlocker on Solana audit by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Unlocker%20Solana%20Audit%201%20-%20CODESPECT.pdf)
* Fractionalizer audit by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/TokenTable%20Fractionalizer%20and%20SellNow%20EVM%20Audit%201%20-%20CODESPECT.pdf)

### $SIGN Token Staking

* The staking contract for $SIGN token has been audited by [CODESPECT](https://raw.githubusercontent.com/EthSign/audit-reports/main/SIGN%20Token%20Staking%20Audit%201%20-%20CODESPECT.pdf)


# Custom Token Claiming Portal

We can create custom token claiming portals to allow your users to claim their tokens. This custom portal will be hosted on your website. By providing custom claiming portals, our goal is to avoid confusion from malicious websites; users will be directed to the true source for all token claiming activities.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FhtySLsYsJR0goH3RDrln%2FSlide%2016_9%20-%201.png?alt=media&amp;token=c8010db3-b012-4be5-8519-61cd1494f2a6" alt=""><figcaption><p>Zetachain and Mocaverse Custom Token Claiming Portals</p></figcaption></figure>


# Airdrop Token Claiming

As an Airdrop token recipient, you can go to <https://claim.tokentable.xyz/airdrop> to see a list of ongoing airdrops and check your eligibility for claiming by clicking on a specific project.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-c2072264599cc46e9ea5a27469e8c358b7ef0c46%2Fairdrop-claiming-list.png?alt=media" alt=""><figcaption></figcaption></figure>

Once you click on one of the ongoing airdrops, you will be taken to the project’s airdrop claiming page to check for eligibility. Be sure to connect to the wallet that is eligible for airdrop. If you are eligible, click on the “Claim Now” button.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-d48607a19aa16c294a409cefa409d799176f6fcd%2Fairdrop-claiming-individual.png?alt=media" alt=""><figcaption></figcaption></figure>


# Unlocker Token Claiming

As token recipient for Unlocker, you can go to <https://claim.tokentable.xyz/unlocker> to see a list of projects you are eligible to claim for.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-6ebe76499663933e3a18f44457ce88254abda062%2Funlocker-claiming-list.png?alt=media" alt=""><figcaption></figcaption></figure>

Once you click on the project in your dashboard, you will be taken to TokenTable's token claiming page for this project. From here, use the “Claim Now” button to claim all currently unlocked tokens.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-784f45c6b1e03bb52ad416489c787f4d228d65fb%2Funlocker-claiming-individual.png?alt=media" alt=""><figcaption></figcaption></figure>

By scrolling down, you may also view on this page the unlocking schedule for tokens released in the future.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-40e9ad3d7ddcf9590fab026d77f8760aa871ffd6%2Funlocker-claiming-individual-details.png?alt=media" alt=""><figcaption></figcaption></figure>


# Airdrop

TokenTable Airdrop empowers users to distribute tokens at a massive scale with powerful optional vesting rules, access control rulesets, and optimized gas cost (e.g. sharding for TON).

### How Does Airdrop Work?

At its core, TokenTable Airdrop uses merkle trees to enable constant-cost token distributions and vesting rules. If a token allocation needs to be split across 6 calendar months to claim, we simply process and pre-split the data into 6 calendar months when generating the merkle tree. As a result, said allocation actually consists of 6 leaf nodes and are treated as 6 separate claims to the smart contract. Our frontend then abstracts this technical detail away, providing users with a smooth experience. Access control rulesets can be implemented via an external hook contract that's called when a claim is made and reverts when certain conditions fail, similar to the one found [here](/for-developers/unlocker/evm/apis/utilities/external-hook). We also use this mechanism to charge fees for token claims.

When the recipient claims their token allocation, a merkle proof is generated by our backend that's then fed to the smart contract. Note that although we generate the proofs, the smart contract ownership itself is assigned to the project owner and there is nothing we can do to assert control over the smart contract.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FZkzOjEJccjFNkaqkyJqu%2FFrame%202085655148.png?alt=media&amp;token=19084311-3829-4b81-8e23-fd09d80f8eeb" alt=""><figcaption><p>An overview of the Airdrop workflow</p></figcaption></figure>


# EVM


# Deployer

## Introduction

MDCreate2 is a factory contract that enables deterministic deployment of distributor instances using `CREATE2`. This ensures predictable contract addresses and provides a unified deployment interface for both Merkle and ECDSA distributors.

## 🚀 Quick Start

### What is MDCreate2?

MDCreate2 is a factory contract that:

* Deploys distributor contracts deterministically using `CREATE2`
* Manages implementation addresses for different distributor types
* Configures fee collectors and fee tokens
* Provides address prediction before deployment

### Key Benefits

* ✅ **Deterministic Addresses** - Predict contract addresses before deployment
* ✅ **Multiple Implementations** - Support various distributor types
* ✅ **Fee Management** - Centralized fee configuration
* ✅ **Gas Efficient** - Minimal proxy pattern for deployments

## 📋 Factory Overview

### Architecture

```
┌────────────────────────────────────────────────────────┐
│                  MDCreate2 Factory                     │
│                                                        │
│  ┌─────────────────┐    ┌──────────────────────────┐   │
│  │ Implementation  │    │    Deployment Mapping    │   │
│  │    Registry     │    │   projectId => address   │   │
│  └─────────────────┘    └──────────────────────────┘   │
│                                                        │
│  ┌─────────────────┐    ┌──────────────────────────┐   │
│  │  Fee Collector  │    │        Fee Tokens        │   │
│  │    Settings     │    │      Configuration       │   │
│  └─────────────────┘    └──────────────────────────┘   │
└────────────────────────────────────────────────────────┘
                         │
                         │ Deploys
                         ▼
      ┌──────────────────────────────────────┐
      │         Distributor Instances        │
      │     (Clones with Minimal Proxies)    │
      └──────────────────────────────────────┘
```

### Key Components

* **Implementation Registry** - Maps distributor types to implementation addresses
* **Deployment Tracking** - Maps project IDs to deployed addresses
* **Fee Configuration** - Manages fee collectors and tokens
* **Clone Factory** - Uses OpenZeppelin's Clones library

## 🔧 Contract Details

### Storage

```solidity
address public defaultFeeCollector;
mapping(string projectId => address deployment) public deployments;
mapping(uint8 mdType => address implementation) public implementations;
mapping(address deployment => address feeCollectors) internal _feeCollectors;
```

### Key Functions

#### Deployment Functions

| Function           | Description                                   |
| ------------------ | --------------------------------------------- |
| `deploy()`         | Deploys a new distributor instance            |
| `simulateDeploy()` | Predicts deployment address without deploying |

```solidity
function deploy(
    uint8 mdType,
    string calldata projectId
) external returns (address instance)
```

#### Configuration Functions

| Function                   | Description                                   |
| -------------------------- | --------------------------------------------- |
| `setImplementation()`      | Registers implementation for distributor type |
| `setDefaultFeeParams()`    | Sets default fee collector                    |
| `setDeploymentFeeParams()` | Sets fee collector for specific deployment    |

#### View Functions

| Function          | Description                          |
| ----------------- | ------------------------------------ |
| `feeCollectors()` | Returns fee collector for deployment |
| `feeTokens()`     | Returns fee token for deployment     |
| `version()`       | Returns factory version              |

## 🔐 Security Considerations

### Access Control

| Role   | Permissions                                  |
| ------ | -------------------------------------------- |
| Owner  | Register implementations, set fee parameters |
| Anyone | Deploy distributors, simulate deployments    |

### Deployment Safety

* **Unique Project IDs** - Each project ID can only be used once
* **Implementation Validation** - Only registered implementations can be deployed
* **Initialization** - Deployed contracts must be initialized by deployer

## 🛠️ CREATE2 Mechanics

### Address Calculation

The deployment address is deterministically calculated using:

```solidity
address = CREATE2(
    0,                              // value
    deployerAddress,                // sender (factory)
    keccak256(projectId),           // salt
    keccak256(implementationCode)   // creation code
)
```

### Benefits

1. **Predictable Addresses** - Know contract address before deployment
2. **Cross-chain Consistency** - Same address on different chains
3. **Counterfactual Instantiation** - Reference contracts before deployment

## 📊 Fee System Integration

### Fee Flow

```
┌─────────────────┐
│   Distributor   │
└────────┬────────┘
         │ Queries fees
         ▼
┌─────────────────┐
│    MDCreate2    │
└────────┬────────┘
         │ Returns collector
         ▼
┌─────────────────┐
│  Fee Collector  │
└─────────────────┘
```

### Fee Configuration

1. **Default Collector** - Applied to all new deployments
2. **Custom Collector** - Override for specific deployments
3. **Fee Tokens** - Retrieved from fee collector contract

## 🔄 Versioning

The factory maintains its own version independent of distributor implementations.

## 📡 Events

| Event       | Description                          |
| ----------- | ------------------------------------ |
| `DidDeploy` | Emitted when distributor is deployed |

```solidity
event DidDeploy(
    uint8 mdType,
    string projectId,
    address instance
);
```

## 🔍 Error Handling

| Error                  | Condition               | Seelctor     |
| ---------------------- | ----------------------- | ------------ |
| `UnsupportedOperation` | Project ID already used | `0x9ba6061b` |

## 📚 Implementation Types

| Type | Description                           |
| ---- | ------------------------------------- |
| 0    | TokenTableMerkleDistributor           |
| 1    | TokenTableNativeMerkleDistributor     |
| 2    | SimpleERC721MerkleDistributor         |
| 3    | NftGatedMerkleDistributor             |
| 4    | CustomFeesNativeMerkleDistributor     |
| 5    | SimpleNoMintERC721MerkleDistributor   |
| 6    | FungibleTokenECDSADistributor         |
| 7    | FungibleTokenWithFeesECDSADistributor |


# Merkle Distributor

## Introduction

The Merkle Distributor provides a modular and efficient solution for token distribution using merkle proofs, supporting various token standards and customizable distribution patterns.

## 🚀 Quick Start

### What is a Merkle Distributor?

A Merkle Distributor is a smart contract system that enables efficient and verifiable token airdrops. Instead of storing all recipient data on-chain, it uses a Merkle tree where only the root is stored, significantly reducing gas costs while maintaining security.

### Key Benefits

* ✅ **Gas Efficient** - Only stores Merkle root on-chain
* ✅ **Verifiable** - Claims are cryptographically proven
* ✅ **Flexible** - Supports multiple token types and distribution patterns
* ✅ **Secure** - Built with security best practices

## 📋 System Overview

### Architecture Diagram

```
┌────────────────────────────────────────────────────┐
│                MDCreate2 Factory                   │
└────────────────────────┬───────────────────────────┘
                         │ Deploys
┌────────────────────────▼───────────────────────────┐
│               BaseMerkleDistributor                │
│      (Abstract base with core functionality)       │
└────────────────────────┬───────────────────────────┘
                         │ Inherited by
        ┌────────────────┴────────────────┐
        │                                 │
┌───────▼──────────┐           ┌──────────▼──────────┐
│ ERC20 Extensions │           │  ERC721 Extensions  │
└──────────────────┘           └─────────────────────┘
```

### Core Components

* **Base Contract** - Core distribution logic
* **Extensions** - Token-specific implementations
* **Custom Extensions** - Special use cases

## 🔧 Core Contracts

### BaseMerkleDistributor

The foundation contract providing core distribution functionality.

#### Key Features

**Storage Pattern (ERC7201)**

The contract uses ERC7201 storage pattern:

```solidity
struct BaseMerkleDistributorStorage {
    mapping(bytes32 leaf => bool used) usedLeafs;
    bytes32 root;
    address deployer;
    address token;
    address claimDelegate;
    uint256 startTime;
    uint256 endTime;
    address claimHook;
}
```

**Claim Mechanism**

Two ways to claim tokens:

1. **Direct Claim** - Recipients claim their own tokens
2. **Delegate Claim** - Authorized delegate claims for recipients

```solidity
function claim(
    bytes32[] calldata proof,
    bytes32 group,
    bytes calldata data,
    bytes calldata extraData
) external payable
```

**Security Features**

* Reentrancy protection on all claim functions
* Double-claim prevention via leaf tracking
* Time-based access control
* Owner-only administrative functions

## 🔌 Extension Contracts

### ERC20 Extensions

#### TokenTableMerkleDistributor

Standard ERC20 token distribution with time-locked claims.

```solidity
struct TokenTableMerkleDistributorData {
    uint256 index;
    uint256 claimableTimestamp;
    uint256 claimableAmount;
}
```

#### TokenTableNativeMerkleDistributor

Distributes native tokens (ETH) with similar functionality.

**Special Features:**

* Handles ETH transfers
* `receive()` function for accepting ETH
* Native token withdrawal support

### ERC721 Extensions

#### SimpleERC721MerkleDistributor

Mints new NFTs to recipients.

**Key Behavior:**

* Creates new tokens via `safeMint`
* No withdrawal function (mints are permanent)

#### SimpleNoMintERC721MerkleDistributor

Distributes pre-existing NFTs.

**Key Behavior:**

* Transfers NFTs held by contract
* Owner can withdraw specific token IDs
* Repurposes `amount` parameter as `tokenId`

### Custom Extensions

#### CustomFeesNativeMerkleDistributor

Native token distributor with fee threshold logic.

**Features:**

* Configurable `feelessThreshold`
* Claims below threshold bypass fees
* Modified claim flow for fee handling

#### NFTGatedMerkleDistributor

Claims gated by NFT ownership.

```solidity
struct NFTGatedMerkleDistributorData {
    TokenTableMerkleDistributorData base;
    uint256 expiryTimestamp;
    uint256 nftTokenId;
}
```

**Special Features:**

* Claims tied to NFT ownership
* Delegate.xyz integration
* Auto-recipient from NFT owner

## 💰 Fee System

### Overview

The fee system provides flexible fee collection for claims.

### Components

```
     ┌─────────────────┐
     │  Claim Request  │
     └────────┬────────┘
              │
              ▼
    ┌────────────────────┐
    │ Fee Collector Set? │
    └─────┬───────┬──────┘
          │       │
      Yes │       │ No
          │       │
          ▼       ▼
    ┌──────────┐ ┌─────────┐
    │Calculate │ │  No     │
    │   Fee    │ │  Fee    │
    └────┬─────┘ └─────────┘
         │
         ▼
    ┌──────────────────┐
    │    Fee Token?    │
    └─────┬───────┬────┘
          │       │
   Native │       │ ERC20
          │       │
          ▼       ▼
    ┌──────────┐ ┌─────────────┐
    │ Deduct   │ │  Transfer   │
    │ from     │ │  from       │
    │ msg.value│ │  Payer      │
    └──────────┘ └─────────────┘
```

### Configuration

1. **Fee Collector** - Address that receives fees
2. **Fee Token** - Native or ERC20 token for fees
3. **Fee Calculation** - External `ITTUFeeCollector` interface

## 🔒 Security

### Access Control

| Role     | Permissions                                           |
| -------- | ----------------------------------------------------- |
| Owner    | Set parameters, withdraw tokens, configure extensions |
| Delegate | Claim on behalf of recipients                         |
| Users    | Claim their allocated tokens                          |

### Security Features

* **Reentrancy Guards** - All state-changing functions protected
* **Leaf Tracking** - Prevents double claims
* **Time Windows** - Enforced distribution periods
* **Proof Verification** - Cryptographic claim validation

## 📡 Events & Errors

### Events

| Event              | Description              |
| ------------------ | ------------------------ |
| `ClaimDelegateSet` | Delegate address updated |
| `Claimed`          | Successful token claim   |

### Errors

| Error                  | Condition             | Selector     |
| ---------------------- | --------------------- | ------------ |
| `UnsupportedOperation` | Operation not allowed | `0x9ba6061b` |
| `TimeInactive`         | Outside claim window  | `0x0c143eb8` |
| `InvalidProof`         | Merkle proof invalid  | `0x09bde339` |
| `LeafUsed`             | Already claimed       | `0x0e4b0ab2` |
| `IncorrectFees`        | Fee amount mismatch   | `0x1669aa83` |


# Signature Distributor

## Introduction

The ECDSA Distributor system provides signature-based token distribution using ECDSA signatures for claim verification. This approach offers an alternative to Merkle tree-based distributions, allowing for more flexible and dynamic claim authorization.

## 🚀 Quick Start

### What is an ECDSA Distributor?

An ECDSA Distributor is a smart contract system that enables efficient token distribution using signature verification. Instead of storing a Merkle tree root, it uses cryptographic signatures from an authorized signer to validate claims, offering greater flexibility for dynamic distributions.

### Key Benefits

* ✅ **Dynamic Claims** - No need to generate Merkle trees in advance
* ✅ **Signature-Based** - Claims authorized via ECDSA signatures
* ✅ **Batch Processing** - Process multiple claims in one transaction

### Key Differences from Merkle Distributors

| Feature      | Merkle Distributor  | ECDSA Distributor |
| ------------ | ------------------- | ----------------- |
| Verification | Merkle proofs       | ECDSA signatures  |
| Data Storage | Merkle root         | Authorized signer |
| Claim Data   | Fixed at deployment | Dynamic per claim |
| Gas Cost     | Higher for setup    | Higher per claim  |
| Flexibility  | Limited             | High              |

## 📋 System Overview

### Architecture Diagram

```
┌─────────────────────────────────────────────────────┐
│                MDCreate2 Factory                    │
└────────────────────────┬────────────────────────────┘
                         │ Deploys
┌────────────────────────▼────────────────────────────┐
│                BaseECDSADistributor                 │
│    (Abstract base with signature verification)      │
└────────────────────────┬────────────────────────────┘
                         │ Inherited by
        ┌────────────────┴────────────────┐
        │                                 │
┌───────▼───────────────────────┐   ┌─────▼──────────────────────┐
│ FungibleTokenECDSADistributor │   │ FungibleTokenWithFeesECDSA │
│                               │   │ Distributor                │
└───────────────────────────────┘   └────────────────────────────┘
```

### Core Components

* **Base Contract** - Core distribution logic with signature verification
* **Standard Extensions** - ERC20 and native token implementations
* **Fee Extensions** - Custom fee handling implementations
* **Factory Integration** - Same MDCreate2 factory for deployment

## 🔧 Core Contracts

### BaseECDSADistributor

The foundation contract providing signature-based distribution functionality.

#### Key Features

**Storage Pattern (ERC7201)**

```solidity
struct BaseECDSADistributorStorage {
    mapping(bytes32 userClaimId => bool claimed) claimedClaims;
    address deployer;
    address authorizedSigner;
    address token;
    address claimHook;
    uint256 startTime;
    uint256 endTime;
}
```

**Claim Mechanism**

Batch claim support with signature verification:

```solidity
function claim(
    address[] calldata recipients,
    bytes32[] calldata userClaimIds,
    bytes[] calldata datas,
    bytes[] calldata signatures,
    bytes[] calldata extraDatas
) external payable
```

**Security Features**

* ECDSA signature verification
* Pausable functionality
* Reentrancy protection
* Claim ID tracking to prevent double claims

#### Key Functions

| Function           | Description                                    |
| ------------------ | ---------------------------------------------- |
| `setBaseParams()`  | Sets token, time window, and authorized signer |
| `setClaimHook()`   | Configures optional claim hook                 |
| `claim()`          | Processes batch claims with signatures         |
| `togglePause()`    | Emergency pause functionality                  |
| `getClaimStatus()` | Check if claims are already processed          |

## 🔌 Extension Contracts

### FungibleTokenECDSADistributor

Standard implementation for ERC20 and native token distribution.

**Data Structure:**

```solidity
struct FungibleTokenECDSADistributorData {
    uint256 claimableTimestamp;
    uint256 claimableAmount;
}
```

**Key Features:**

* Supports both ERC20 and native tokens
* Time-locked claims based on `claimableTimestamp`
* Standard withdrawal function for unclaimed tokens

### FungibleTokenWithFeesECDSADistributor

Extension that includes fee data in the claim structure.

**Data Structure:**

```solidity
struct FungibleTokenWithFeesECDSADistributorData {
    uint256 claimableTimestamp;
    uint256 claimableAmount;
    uint256 fees;
}
```

**Key Features:**

* Fees encoded in claim data
* Batch fee collection
* Mandatory fee collector configuration
* Custom fee amounts per claim

## 💰 Fee System

### Overview

The ECDSA distributor system supports flexible fee collection with different implementations.

### Fee Flow

```
    ┌─────────────────┐
    │  Batch Claims   │
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │ Calculate Total │
    │      Fees       │
    └────────┬────────┘
             │
             ▼
    ┌──────────────────┐
    │  Fee Collector   │
    │  Set?            │
    └─────┬───────┬────┘
          │       │
    Yes   │       │   No
          │       │
          ▼       ▼
    ┌──────────┐ ┌─────────┐
    │ Charge   │ │  Skip   │
    │ Fees     │ │  Fees   │
    └────┬─────┘ └─────────┘
         │
         ▼
    ┌──────────────────┐
    │    Fee Token?    │
    └─────┬───────┬────┘
          │       │
   Native │       │ ERC20
          │       │
          ▼       ▼
    ┌──────────┐ ┌─────────────┐
    │ Transfer │ │  Transfer   │
    │ ETH      │ │  Tokens     │
    └──────────┘ └─────────────┘
```

### Fee Implementations

1. **Fixed Fee** - Base implementation charges per claim or batch
2. **Custom Fee** - Extension allows per-claim fee amounts

## 🔒 Security

### Access Control

| Role              | Permissions                                    |
| ----------------- | ---------------------------------------------- |
| Owner             | Set parameters, toggle pause, configure signer |
| Authorized Signer | Sign claim authorizations                      |
| Users             | Submit claims with valid signatures            |

### Security Features

* **Signature Verification** - ECDSA signature validation
* **Pausable** - Emergency stop functionality
* **Reentrancy Guards** - Protection against reentrancy attacks
* **Claim Tracking** - Prevents double claiming
* **Time Windows** - Enforced distribution periods

## 📡 Events & Errors

### Errors

| Error                  | Condition                     | Selector     |
| ---------------------- | ----------------------------- | ------------ |
| `UnsupportedOperation` | Operation not allowed         | `0x9ba6061b` |
| `TimeInactive`         | Outside distribution window   | `0x0c143eb8` |
| `InvalidSignature`     | Signature verification failed | `0x8baa579f` |
| `ClaimClaimed`         | Claim already processed       | `0xadeff36f` |
| `IncorrectFees`        | Fee amount mismatch           | `0x1669aa83` |
| `FeeCollectorNotSet`   | Missing fee collector         | `0xb4b53f42` |


# Solana

The Solana airdrop token distributor is designed to facilitate the efficient distribution of tokens on the Solana blockchain. Modeled after its counterpart developed in Solidity for Ethereum, the Solana token distributor leverages the high-speed and low-cost capabilities of Solana to streamline token distribution processes. The Solana airdrop distributor has been developed into several variants, making it ideal for a variety of use cases, including airdrops, rewards, and other token allocation needs. Solana token distributors have been integrated with a custom fee collector program offering flexibility and fine-grained control over unique fee structures for recipients and projects.


# Merkle Distributor

### Introduction

The Merkle Distributor provides a modular and efficient solution for token distribution using merkle proofs, supporting various token standards and customizable distribution patterns.

## 🚀 Quick Start

### What is a Merkle Distributor?

A Merkle Distributor is a smart contract system that enables efficient and verifiable token airdrops. Instead of storing all recipient data on-chain, it uses a Merkle tree where only the root is stored, significantly reducing gas costs while maintaining security.

### Key Benefits

* ✅ **Gas Efficient** - Only stores Merkle root on-chain
* ✅ **Verifiable** - Claims are cryptographically proven
* ✅ **Flexible** - Supports multiple token types and distribution patterns
* ✅ **Secure** - Built with security best practices

## 📋 System Overview

### Account Structure

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FAi29LrvdNzCzdpUBcIzX%2FUntitled%20diagram%20_%20Mermaid%20Chart-2025-07-08-190045.png?alt=media&amp;token=4b5702ee-52f5-41ab-b94f-250629ca09da" alt=""><figcaption></figcaption></figure>

### Storage Pattern

A contract has one config storage account, which stores the contract admin's account address and the default fee collector's deployment address. This account will also store a new admin's account address for secure two-step ownership transfers.

```rust
pub struct Config {
  pub admin: Pubkey,
  pub new_admin: Pubkey,
  pub default_fee_collector: Pubkey,
}
```

A Merkle Distributor deployment can manage multiple airdrops. Airdrops use the following storage pattern:

```rust
pub struct MerkleAirdrop {
  pub owner: Pubkey,
  pub root: [u8; 32],
  pub project_token: Pubkey,
  pub claim_delegate: Pubkey,
  pub start_time: u64,
  pub end_time: u64,
  pub fee_collector: Pubkey,
  pub fee_token: Pubkey,
  pub paused: bool,
  pub uri: String,
}
```

A standard time-locked claim is created with the following storage pattern:

```rust
pub struct TokenTableMerkleDistributorData {
  pub index: u64,
  pub claimable_timestamp: u64,
  pub claimable_amount: u64,
}
```

### Claim Mechanism

There are two ways to claim tokens:

1. **Direct Claim** - Recipients claim their own tokens
2. **Delegate Claim** - Authorized delegate claims for recipients

```rust
pub fn claim(
  ctx: Context<Claim>,
  project_id: String,
  leaf: [u8; 32],
  proof: Vec<[u8; 32]>,
  group: [u8; 32],
  data: TokenTableMerkleDistributorData
) -> Result<()>
```

### **Security Features**

* Reentrancy protection on all claim functions
* Double-claim prevention via leaf tracking
* Time-based access control
* Owner-only administrative functions

## 💰 Fee System

### Overview

The fee system provides flexible fee collection for claims.

### Components

1. **Fee Collector** - External `FeeCollector` program that calculates and manages vault accounts
2. **Fee Token** - Native or SPL token for fees
3. **Vault Accounts** - Program Derived Accounts (PDAs) for holding fee tokens

## 🔒 Security

### Access Control

| Role          | Permissions                                                         |
| ------------- | ------------------------------------------------------------------- |
| Program Admin | Set fee collector, transfer program admin                           |
| Airdrop Owner | Set airdrop parameters, withdraw tokens, transfer airdrop ownership |
| Delegate      | Claim on behalf of recipients                                       |
| Users         | Claim their allocated tokens                                        |

### Security Features

* **Reentrancy Guards** - All state-changing functions protected
* **Leaf Tracking** - Prevents double claims
* **Time Windows** - Enforced distribution periods
* **Proof Verification** - Cryptographic claim validation

## 📡 Events & Errors

### Events

| Event              | Description                      |
| ------------------ | -------------------------------- |
| `ClaimDelegateSet` | Delegate address updated         |
| `Claimed`          | Successful token claim           |
| `Initialized`      | Successful aidrop initialization |

### Errors

| Error                       | Condition                                   |
| --------------------------- | ------------------------------------------- |
| `UnsupportedOperation`      | Operation not allowed                       |
| `TimeInactive`              | Outside airdrop claim window                |
| `InvalidSignature`          | Signature invalid                           |
| `LeafUsed`                  | Already claimed                             |
| `IncorrectFees`             | Fee amount mismatch                         |
| `OutsideClaimableTimeRange` | Outside claim window                        |
| `NotOwner`                  | Not airdrop owner                           |
| `NotProgramAdmin`           | Not program admin                           |
| `NotPermissioned`           | Missing permissions for user                |
| `InvalidFeeCollector`       | Provided fee collector account is incorrect |
| `Paused`                    | Airdrop is paused                           |


# Signature Distributor

## Introduction

The EDDSA Distributor program provides signature-based token distribution using EDDSA signatures for claim verification. This approach offers an alternative to Merkle tree-based distributors, allowing for more flexible and dynamic claim authorizations.

## 🚀 Quick Start

### What is an EDDSA Distributor?

An EDDSA Distributor is a smart contract system that enables efficient token distribution using signature verification. Instead of storing a Merkle tree root, it uses cryptographic signatures from an authorized signer to validate claims, offering greater flexibility for dynamic distributions.

### Key Benefits

* ✅ **Dynamic Claims** - No need to generate Merkle trees in advance
* ✅ **Signature-Based** - Claims authorized via EDDSA signatures
* ✅ **Batch Processing** - Process multiple claims in one transaction

### Key Differences from Merkle Distributors

| Feature      | Merkle Distributor  | EDDSA Distributor |
| ------------ | ------------------- | ----------------- |
| Verification | Merkle proofs       | EDDSA signatures  |
| Data Storage | Merkle root         | Authorized signer |
| Claim Data   | Fixed at deployment | Dynamic per claim |
| Gas Cost     | Higher for setup    | Higher per claim  |
| Flexibility  | Limited             | High              |

## 📋 System Overview

### Account Structure

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FggMWCMJIovaYYaVJ9jow%2FUntitled%20diagram%20_%20Mermaid%20Chart-2025-07-08-190329.png?alt=media&amp;token=7bfb322f-16b4-4d9c-a827-490a72a3392d" alt=""><figcaption></figcaption></figure>

### Storage Pattern

A contract has one config storage account, which stores the contract admin's account address and the default fee collector's deployment address. This account will also store a new admin's account address for secure two-step ownership transfers.

```rust
pub struct Config {
  pub admin: Pubkey,
  pub new_admin: Pubkey,
  pub default_fee_collector: Pubkey,
}
```

An EDDSA Distributor deployment can manage multiple airdrops. Airdrops use the following storage pattern:

```rust
pub struct Airdrop {
  pub owner: Pubkey,
  pub authorized_signer: Pubkey,
  pub token: Pubkey,
  pub start_time: u64,
  pub end_time: u64,
  pub fee_collector: Pubkey,
  pub fee_token: Pubkey,
  pub paused: bool,
}
```

### Claim Mechanism

Claim support with signature verification:

```rust
pub fn claim(
  ctx: Context<Claim>,
  project_id: String,
  recipient: Pubkey,
  user_claim_id: [u8; 32],
  group: [u8; 32],
  user_claim_data: EDDSADistributorData,
  signature: Vec<u8>
) -> Result<()>
```

**Security Features**

* ECDSA signature verification
* Pausable functionality
* Reentrancy protection
* Claim ID account initialization to prevent double claims

#### Key Functions

| Function          | Description                                     |
| ----------------- | ----------------------------------------------- |
| `initialize()`    | Initialize an airdrop, create required accounts |
| `setBaseParams()` | Sets token, time window, and authorized signer  |
| `claim()`         | Processes batch claims with signatures          |
| `togglePause()`   | Emergency pause functionality                   |
| `deposit()`       | Deposit tokens to be airdropped                 |

## 🔌 Distributor Variants

### EDDSADistributor

Standard implementation for token distribution.

**Data Structure:**

```rust
pub struct EDDSADistributorData {
  pub claimable_timestamp: u64,
  pub claimable_amount: u64,
}
```

**Key Features:**

* Supports both SPL and SPL2022 tokens
* Time-locked claims based on `claimable_timestamp`
* Standard withdrawal function for unclaimed tokens

### FungibleTokenWithFeesEDDSADistributor

Extension that includes fee data in the claim structure.

**Data Structure:**

```rust
pub struct EDDSADistributorData {
  pub claimable_timestamp: u64,
  pub claimable_amount: u64,
  pub fees: u64,
}
```

**Key Features:**

* Fees encoded in claim data
* Batch fee collection
* Mandatory fee collector configuration
* Custom fee amounts per claim

## 💰 Fee System

### Overview

The fee system provides flexible fee collection for claims.

### Components

1. **Fee Collector** - External `FeeCollector` program that calculates and manages vault accounts
2. **Fee Token** - Native or SPL token for fees
3. **Vault Accounts** - Program Derived Accounts (PDAs) for holding fee tokens

### Fee Implementations

1. **Fixed Fee** - Base implementation charges per claim or batch
2. **Custom Fee** - Extension allows per-claim fee amounts

## 🔒 Security

### Access Control

| Role              | Permissions                                    |
| ----------------- | ---------------------------------------------- |
| Program Admin     | Set fee collector, transfer program admin      |
| Airdrop Owner     | Set parameters, toggle pause, configure signer |
| Authorized Signer | Sign claim authorizations                      |
| Users             | Submit claims with valid signatures            |

### Security Features

* **Signature Verification** - ECDSA signature validation
* **Pausable** - Emergency stop functionality
* **Reentrancy Guards** - Protection against reentrancy attacks
* **Claim Tracking** - Prevents double claiming
* **Time Windows** - Enforced distribution periods

## 📡 Events & Errors

### Events

| Event              | Description                      |
| ------------------ | -------------------------------- |
| `Initialized`      | Successful aidrop initialization |
| `ClaimDelegateSet` | Successful claim delegate set    |
| `Claimed`          | Successful token claim           |

### Errors

| Error                       | Condition                                                                   |
| --------------------------- | --------------------------------------------------------------------------- |
| `UnsupportedOperation`      | Operation not allowed                                                       |
| `TimeInactive`              | Outside distribution window                                                 |
| `InvalidSignature`          | Signature verification failed                                               |
| `OutsideClaimableTimeRange` | Outside claim window                                                        |
| `NotOwner`                  | Unpermissioned account trying to call an owner-permissioned function        |
| `NotProgramAdmin`           | Unpermissioned account trying to call a program admin-permissioned function |
| `NotPermissioned`           | Unpermissioned account trying to receive program admin status               |
| `InvalidFeeCollector`       | Provided fee collector account is incorrect                                 |
| `Paused`                    | Airdrop has been paused                                                     |


# TON

This is technical documentation for the TON smart contract implementation of the TokenTable product.

### **About TokenTable TVM**

TokenTable TVM is a powerful and versatile smart contract application implemented on the TON blockchain. It is specifically designed for efficient token management and distribution. Whether you're a newcomer looking to understand its token handling capabilities or an experienced developer seeking advanced configuration options for token distribution, this documentation serves as your comprehensive guide to leveraging the full potential of TokenTable TVM.

### **Key Features**

TokenTable TVM offers four key features designed to meet diverse user needs:

* **Token Distribution**: Efficiently manage and distribute tokens on the TON blockchain.
* **Time-Specific Disbursement**: Set precise start and end dates for token distribution periods.
* **Flexible Fee Structure**: Implement claim fees or TON token fees as needed.
* **No Claim Fee Thresholding**: Option to set a threshold below which no claim fee is charged.

### **How to Use This Documentation**

To make the most of this documentation, follow these guidelines:

* **Search**: Use the search bar to quickly find information on specific topics.
* **Navigation**: The documentation is organized into sections, so you can navigate through the table of contents to locate the information you need.
* **Links**: We've included links to related articles and resources for in-depth exploration.
* **Feedback**: If you can't find the information you're looking for or have suggestions for improvement, please let us know.

### **Contacting Support**

If you encounter any issues or have questions not covered in this documentation, visit our support portal at [TokenTable](https://docs.tokentable.xyz/support/feedback-and-troubleshooting).


# Getting Started

Welcome to TokenTable TVM! This section provides a basic understanding of our token distribution smart contract before you dive into the detailed documentation. We'll cover the essential concepts and prerequisites to help you make the most of this guide.

### **System Requirements**

Before starting the development of the TokenTable TVM smart contract on your local machine, ensure your environment meets the following requirements:

#### Operating Systems

* Windows 10 or later
* macOS 10.14 or later
* Ubuntu 18.04 LTS or later

#### NodeJS and NPM

Our application uses the [TON Blueprint Javascript SDK](https://docs.ton.org/develop/smart-contracts/sdk/javascript) for development, so you need Node.js and NPM on your machine:

* Install Node.js (version 14.x or later recommended)
* Ensure NPM (Node Package Manager) is installed for managing dependencies.

To verify the installation:

```bash
node -v
npm -v
```

### **Installation and Setup**

To start using our smart contract, follow the steps (might vary based on your operating system):

1. Clone our `tokentable-tvm` repository.
2. Run `npm i` , make sure you are using node `v18.19.0`.
3. Optionally, make a `.env` file with the necessary variables.

***

That concludes the "Getting Started" section. As you continue through this documentation, you'll find more in-depth information on various aspects of our smart contract, including its architecture, configuration, and usage.


# Architecture

This section provides a high-level overview of the smart contract's architecture. Understanding how the smart contract is structured is essential for developers, administrators, and anyone who wants to customize or maintain the system.

### **High-Level Overview**

The TokenTable TVM smart contract is designed as a scalable, secure, and efficient solution for token distribution on the TON blockchain. It leverages **merkle proofs** for wallet address verification, ensuring trustless and cost-effective token distribution. Here’s an overview:

* **Merkle Proof Architecture**: The core of the contract uses merkle proofs to verify user wallets before token distribution. This enables efficient verification without exposing the entire user list on-chain, ensuring both privacy and reduced gas costs.
* **Token Distribution Logic**: Once a user's wallet is verified through a merkle proof, the smart contract initiates the token distribution process, adhering to preset rules and conditions.
* **Blockchain Interaction**: All interactions with the blockchain, including wallet address verification and token distribution, are handled on-chain through TON's secure, decentralized network.
* **Security**: The contract is designed with strict validation mechanisms, leveraging cryptographic proofs to ensure only eligible users receive tokens, providing transparency and security.

### **Component Contracts**

The TokenTable TVM smart contract solution is structured around key contract components, each designed to fulfill a specific role in the token distribution process using merkle proofs. Below is an overview of the core components:

#### Main Distributor Contract

* **Primary Role**: The **Main Distributor Contract** manages the overall token distribution process. It holds the root hash of the Merkle tree, which is used to verify user wallets against a predefined list of eligible recipients.
* **Merkle Root Management**: The merkle root is securely stored in this contract, acting as the reference point for wallet verification during token claims.
* **Token Distribution Logic**: Upon receiving a valid merkle proof from a user (submitted through the leaf), the contract verifies the proof against the stored merkle root. If the verification is successful, the contract initiates token distribution to the user's wallet.

#### Leaf Contracts

* **Wallet Verification**: **Leaf Contracts** are designed to validate individual wallets by processing merkle proofs generated off-chain. Each contract represents a "leaf" node in the merkle tree, corresponding to an individual user wallet.
* **Proof Submission**: Users interact with their corresponding Leaf Contract by submitting a merkle proof, which the contract uses to confirm their eligibility based on the data in the merkle tree.
* **Security and Efficiency**: By using merkle proofs, the Leaf Contracts efficiently verify large sets of users without requiring extensive on-chain data, reducing cost and execution time.
* **Token Claim Execution**: Once a wallet address is verified, the Leaf Contract communicates with the **Main Distributor Contract** to trigger the actual token distribution.

#### Additional Features of the Contracts

* **Data Integrity**: The use of cryptographic merkle trees ensures data integrity, allowing the contract to securely verify claims without revealing sensitive information.
* **Scalability**: Both the Main Distributor and Leaf Contracts are designed to handle high volumes of users, ensuring scalable token distribution across a wide network.
* **Gas Optimization**: The contracts minimize gas costs by processing wallet verification off-chain (via Merkle proofs) and only executing the necessary steps on-chain.

### **Sequence Diagram**

For a more detailed understanding of how data flows within the application, we've prepared a data flow diagram:

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2Fgit-blob-25eb22ffb203b339a7e67f1fcbb2a50c70557b0a%2Fsequence-diagram.png?alt=media" alt=""><figcaption><p>Sequence Diagram</p></figcaption></figure>

Data flows from a user claim into the leaf contract, to the main contract, and then to the user wallet. Understanding this data flow is helpful for developers who need to work on integrations or customize the system.

***

Understanding the architecture and components of our web application is crucial for developers and administrators, as it forms the foundation for customization, troubleshooting, and scaling. In the following sections, we'll delve into specific aspects of usage.


# Usage

This section guides you through the **TokenTable TVM smart contract** for efficient token distribution based on merkle proof verification. Whether you're a developer deploying the contract or a user claiming tokens, this section provides key instructions for each role.

### **Smart Contract Interaction Overview**

#### **Contract Structure**

The **TokenTable TVM** consists of two primary contracts:

* **Main Distributor Contract**: Handles token distribution logic and stores the merkle root for verification.
* **Leaf Contracts**: Verifies individual merkle proofs and triggers token distribution to valid users.

#### **Deployment and Setup**

Before you begin interacting with the contract, ensure you’ve deployed the **Main Distributor Contract** and associated **Leaf Contracts**. Here’s a step-by-step process:

1. **Deploy the Main Distributor Contract**:
   * The merkle root (generated off-chain) must be provided during deployment.
   * Ensure the correct merkle tree configuration to verify users' eligibility for token claims.
2. **Deploy the Leaf Contracts**:
   * Each Leaf Contract corresponds to a specific set of users (or leaf nodes) in the merkle tree.
   * These contracts will interact with the Main Distributor Contract to validate user claims and distribute tokens.
3. **Initialize Token Distribution**:
   * Ensure the contracts are funded with sufficient tokens to execute the distribution.

#### **Claiming Tokens**

Users will interact with the contract to claim tokens. Below is a guide on how to submit a claim using a merkle proof:

1. **Generating Merkle Proof**:
   * Off-chain, generate a merkle proof for the user’s wallet address.
   * Ensure the proof corresponds to the merkle root stored in the Main Distributor Contract.
2. **Submitting Proof**:
   * The user submits their merkle proof and index directly through a smart contract interaction to their corresponding leaf.
   * The proof is validated by the contract, ensuring the user is eligible for token distribution.
3. **Token Distribution**:
   * Upon successful verification, the contract transfers the appropriate number of tokens to the user’s wallet.
   * If the proof is invalid, the contract will reject the claim and unlock the leaf ready for reclaiming.

### **Contract Roles and Permissions**

The **TokenTable TVM** smart contract enforces certain roles and permissions to maintain secure and efficient operations:

* **Admin**: The administrator has the authority to deploy the contracts, set up merkle roots, and manage the token distribution process.
* **User**: Users submit their merkle proofs to claim tokens. They can only interact with their corresponding **Leaf Contracts** and cannot alter contract configurations.
* **Fee Admin**: This administrator has the authority to collect accumulated claim fees to their wallets.

### **Common Use Cases**

Here are some scenarios illustrating how different participants may interact with the TokenTable TVM smart contract:

#### **Use Case 1: Token Distribution for an Airdrop**

A project running an airdrop campaign can use **TokenTable TVM** to distribute tokens securely to a large user base by:

* Deploying the **Main Distributor Contract** with a predefined merkle root.
* Users can claim tokens by submitting their merkle proof, ensuring only eligible wallets receive tokens.

#### **Use Case 2: Verifying Wallet Ownership**

A company can utilize the **Leaf Contracts** to verify wallet ownership through cryptographic proofs without needing to store or expose sensitive data. Users can simply submit their proof to prove ownership and receive rewards.

#### **Use Case 3: Decentralized Token Rewards**

For decentralized platforms, **TokenTable TVM** enables efficient token reward distribution to users without requiring an extensive on-chain user list. By leveraging merkle proofs, the system remains scalable and cost-efficient.

#### **Use Case 4: Batch Token Distribution**

Organizations distributing tokens to many users can use batch processing by splitting the distribution into multiple **Leaf Contracts**. This approach helps optimize gas costs and ensures the system can handle a large number of transactions.

***

In the following sections, we'll delve into more technical aspects, such as TLB structure and relationships.


# Smart Contract Schema

Understanding the contract schema is essential for developers working with the **TokenTable TVM**. This section provides an overview of the TLB (Type-Level Binary) schema, which defines the structure and relationships of the smart contract components for interaction within the TON blockchain.

### **TLB Structure**

The **TokenTable TVM** smart contract uses TLB to define the serialization format and logic for its data structures and functions. Below is the schema breakdown of the contract’s key components:

#### **Main Distributor Contract**

The **Main Distributor Contract** holds the merkle root for wallet verification and manages token distribution. Here’s the TLB schema for the contract:

```
main_distributor#_
    version:int             ;; Contract version
    admin_address:slice     ;; Admin address with management privileges
    root:int                ;; Merkle root for verifying claims
    deployer:slice          ;; Deployer address, initial contract creator
    token:slice             ;; Main Contract Jetton Wallet for distribution
    start_time:int          ;; Start time for token distribution
    end_time:int            ;; End time for token distribution
    leaf_code:cell          ;; Code for the leaf contracts
    claim_fee:int           ;; Fee required to submit a claim
    claim_fee_address:slice ;; Address to receive the claim fees
    paused:int              ;; Distribution paused status (1 = paused, 0 = active)
    feeless_threshold:int   ;; Claim threshold below which no fee is charged
    fee_accumulated:int     ;; Total fees accumulated from claims
    -> MainDistributor;
```

#### **Leaf Contract**

Each **Leaf Contract** verifies individual wallet claims by processing merkle proofs and triggering token transfers. Here’s the TLB schema for a leaf contract:

```
leaf_contract#_
    used:int               ;; Status indicating if the leaf has been used for a 
			   ;; claim (1 = used, 0 = unused)
    distributor:slice      ;; Address of the main distributor contract
    index:int              ;; Index of the leaf in the merkle tree
    -> LeafContract;
```

#### **Claim Transaction**

The claim transaction initiates the process of submitting a merkle proof and distributing tokens. Below is the TLB schema for submitting a claim:

```
claim_operation#_
    proof:cell          ;; Reference to the merkle proof (cell)
    index:uint64        ;; Index of the leaf in the merkle tree
    -> ClaimOperation;
```

#### **Token Transfer**

This defines the structure for token transfers triggered after successful claim verification:

```
token_transfer#_
    to_addr:bits256     ;; Recipient wallet address
    amount:uint64       ;; Amount of tokens to transfer
    -> TokenTransfer;
```

### **TLB Relationships**

To visualize the relationships between the TLB components, here's a conceptual breakdown:

* **Main Distributor Contract** holds the **merkle root** and manages the main jetton wallet.
* **Leaf Contracts** handle individual wallet verification using **merkle proofs** and communicate with the Main Distributor Contract for token transfers.
* **Claim Transactions** are submitted by users with **merkle proofs** and are processed by the Leaf Contracts.
* **Token Transfers** occur once the merkle proof is verified, resulting in the distribution of tokens to the user’s wallet.

***

In the following sections, we'll explore integration capabilities of the smart contract.


# Integration

Integrating **ton-connect** enhances user interactions with the TON blockchain in your DApp. Below are the steps for integrating **ton-connect** using **React**, **Vue**, and **HTML/JavaScript (Vanilla)**.

### **Frontend Integration with ton-connect**

#### **1. React Integration**

#### Installation

To integrate **TON Connect** into your React application, install the **@tonconnect/ui-react** package:

```bash
npm install @tonconnect/ui-react
```

#### TON Connect Initiation

Wrap your application with `TonConnectUIProvider`, passing the manifest URL:

```jsx
import { TonConnectUIProvider } from '@tonconnect/ui-react';

export function App() {
    return (
        <TonConnectUIProvider manifestUrl="https://<YOUR_APP_URL>/tonconnect-manifest.json">
            { /* Your app */ }
        </TonConnectUIProvider>
    );
}
```

#### Connect to the Wallet

Add the `TonConnectButton` to allow users to connect their wallets:

```jsx
import { TonConnectButton } from '@tonconnect/ui-react';

export const Header = () => {
    return (
        <header>
            <span>My App with React UI</span>
            <TonConnectButton />
        </header>
    );
};
```

#### **2. Vue Integration**

#### Installation

To start integrating **TON Connect** into your Vue application, install the **@townsquarelabs/ui-vue** package:

```bash
npm install @townsquarelabs/ui-vue
```

#### TON Connect Initiation

Wrap your application with `TonConnectUIProvider`, specifying the manifest URL in the options:

```html
<template>
  <TonConnectUIProvider :options="options">
    <!-- Your app -->
  </TonConnectUIProvider>
</template>

<script>
import { TonConnectUIProvider } from '@townsquarelabs/ui-vue';

export default {
  components: { TonConnectUIProvider },
  setup() {
    const options = {
      manifestUrl: "https://<YOUR_APP_URL>/tonconnect-manifest.json",
    };
    return { options };
  }
}
</script>
```

#### Connect to the Wallet

Use the `TonConnectButton` to enable wallet connections:

```html
<template>
  <header>
    <span>My App with Vue UI</span>
    <TonConnectButton />
  </header>
</template>

<script>
import { TonConnectButton } from '@townsquarelabs/ui-vue';

export default {
  components: { TonConnectButton }
}
</script>
```

#### **3. HTML/JavaScript (Vanilla) Integration**

#### Installation

To integrate **TON Connect** into your HTML/JavaScript application, add the script in the `<head>` of your HTML:

```html
<script src="<https://unpkg.com/@tonconnect/ui@latest/dist/tonconnect-ui.min.js>"></script>
```

#### TON Connect Initiation

Add a button in your HTML to connect to the wallet:

```html
<div id="ton-connect"></div>

<script>
    const tonConnectUI = new TON_CONNECT_UI.TonConnectUI({
        manifestUrl: 'https://<YOUR_APP_URL>/tonconnect-manifest.json',
        buttonRootId: 'ton-connect'
    });
</script>
```

#### Connect to the Wallet

The "Connect" button will automatically handle clicks. You can also open the connect modal programmatically:

```html
<script>
    async function connectToWallet() {
        const connectedWallet = await tonConnectUI.connectWallet();
        console.log(connectedWallet); // Handle the connected wallet as needed
    }

    // Call the function to connect
    connectToWallet().catch(error => {
        console.error("Error connecting to wallet:", error);
    });
</script>
```

***


# Unlocker

TokenTable Unlocker provides users with a secure, self-custodial token management and unlocking experience on EVM, Solana, and Starknet. To best illustrate the entire process, here is an example of the core user workflow, which involves all core smart contracts.

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2F0IXs4aZFP1MIrQ78Xyrv%2FFrame%202085655147.png?alt=media&amp;token=ee66ebf8-dfbe-44de-9ec3-8838713a7d6f" alt=""><figcaption><p>An overview of the Unlocker workflow</p></figcaption></figure>

*Note: all time units are in seconds, including timestamps.*

## Context

Suppose a startup has fundraised, hired its core team, and uses TokenTable to distribute tokens to its investors and employees. Founders must create and enforce unlocking schedules for their investor/team tokens. Here is a step-by-step guide to accomplish this.

Suppose the unlocking schedule looks like the graph below:

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FaUeWZ802uPXbs5R33PlE%2FTTUV2_230620_192109.jpg?alt=media&amp;token=40dae58a-7ea1-454b-882f-26b97fed4973" alt=""><figcaption><p>x: time, y: percentage of tokens unlocked</p></figcaption></figure>

## Founder Workflow

### 1. Deploying a TokenTable Suite

Each startup or organization must deploy its own instance of TokenTable. After deployment, they can use the same instance over multiple unlocking schedules. To deploy, call `deployTTSuite(...)` .

### 2. Creating a Preset Unlocking Schedule

A **preset** contains shared information across all stakeholders within the same unlocking schedule. For example, in the same fundraising round, all investors follow the same unlocking curves and claim intervals. To create a preset, call `createPresets(...)`.

The input parameters to this function are fairly complex:

* `presetId` can be set to whatever you want as long as it doesn't exist yet
* `linearStartTimestampsRelative` records the start timestamp of the beginning of each linear segment. Cliff releases are treated as linears with a duration of 1 while cliff waiting periods are treated as linears with an unlocking percentage of 0%. In short, everything is a linear segment. Relative means the timestamp is relative to the start time, which isn't specified in the preset.
* `linearEndTimestampRelative` is the end timestamp of the final linear segment.
* `linearBips` is the number of basis points (bips) each linear segment unlocks. As mentioned above, cliff waiting periods unlock 0 bips. All bips should add up to the hardcoded `BIPS_PRECISION` variable, which is 10000.
* `numOfUnlocksForEachLinear` is the number of unlocks within each linear segment. The minimum value is 1 or else the claim function will always revert. Use 1 for cliff waits and releases.
* `stream` determines if unlocked tokens are made available to recipients in discrete unlocks or constant streams.

Here is what the input parameters look like for the unlocking graph above:

```solidity
linearStartTimestampsRelative: [0, 10, 11, 30, 31, 40, 41, 60, 90],
linearEndTimestampRelative: 130,
linearBips: [0, 1000, 0, 1000, 0, 2000, 0, 2000, 4000],
numOfUnlocksForEachLinear: [1, 1, 1, 1, 1, 1, 1, 3, 4],
stream: false
```

Here are some more input parameters for different unlocking curves:

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FKm9epy6RvL54cVjhpiQ3%2FPasted%20Graphic%201.png?alt=media&amp;token=631a8275-86c8-4ee4-bbd2-8104b07657d4" alt=""><figcaption><p>linearStartTimestampsRelative: [0,31536000], linearEndTimestamp: 31536001, linearBips: [0,10000], numOfUnlockerForEachLinear: [1,1]</p></figcaption></figure>

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FwW3j9QEQmtUpmqDQPCEn%2FPasted%20Graphic.png?alt=media&amp;token=b9072d79-db32-4497-ac0e-8bdd73ef87ac" alt=""><figcaption><p>linearStartTimestampsRelative: [0,1,7747200], linearEndTimestamp: 36604800, linearBips: [1300,1400,7300], numOfUnlockerForEachLinear: [1,1,56]</p></figcaption></figure>

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2F8QnhvZHc1HcZtpaTSs7R%2FPasted%20Graphic%202.png?alt=media&amp;token=783d4f3a-2ea7-44ef-a315-66f9cccd1360" alt=""><figcaption><p>linearStartTimestampsRelative: [0,126230400], linearEndTimestamp: 126230401, linearBips: [10000,0], numOfUnlockerForEachLinear: [48,1]</p></figcaption></figure>

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FtjhuCmF0gGZlgN5Cem9q%2FPasted%20Graphic%203.png?alt=media&amp;token=49b93ca9-c88d-4969-97ee-92ef5436d351" alt=""><figcaption><p>linearStartTimestampsRelative: [0,1,2678400,5100278,7783778,7791562,5120954], linearEndTimestamp: 5126074, linearBips: [1600,200,3300,600,300,500,3500], numOfUnlockerForEachLinear: [1,1,2,3,3,8,2]</p></figcaption></figure>

### 3. Creating an Actual Unlocking Schedule

After we create a **preset**, we can create an **actual**. An actual is based on a preset but contains information unique to a single token recipient, such as the total locked token amount and start time. To create an actual, call `createActuals(...)`.

An example of input parameters is as follows:

```solidity
recipients: [0xd8da6bf26964af9d7eed9e03e53415d37aa96045],
actuals: [{presetId: keccak256('seed'), startTimestampAbsolute: 1687263601, amountClaimed: 0, totalAmount: 10000}],
recipientIds: [0],
batchId: 0,
extraData: 0x
```

* `recipients` will receive the unlocking schedules.
* `actuals` is an array of `Actual` structs:
  * `presetId` is the preset you intend to use (created in the previous step).
  * `startTimestampAbsolute` is the start time of this actual schedule. This is a standard UNIX timestamp and must be in the future. You can get the current timestamp here: <https://www.unixtimestamp.com/>
  * `amountClaimed` indicates how much unlocking this schedule has already done. This is only useful if you are migrating your unlocking system from a different platform. Setting this number allows you to continue the unlocking progress instead of starting over.
  * `totalAmount` is the total number of tokens to be unlocked.
* `recipientIds`: This is only emitted as an event for TokenTable frontend use.
* `batchId`: This is only emitted as an event for TokenTable frontend use.
* `extraData`: This is passed to the hook directly.

This function safely mints a **FutureToken NFT** to the recipient's address. The FutureToken determines where the unlocked tokens go to, not the recipient's address. Unlocked tokens are sent to the owner of the corresponding FutureToken and this token can be transferred to a different address by the original recipient.

The token ID (which is the `actualId`) of the minted FutureToken can be observed through an emitted event in the transaction receipt.

### 4. Depositing Funds into an Actual Unlocking Schedule

To deposit tokens into the unlocking contract, simply perform a standard ERC-20 `transfer(...)` with TokenTable Unlocker as `to`. Partial deposit is supported but if the claimable amount is greater than the amount deposited, the claim action will revert. To prevent this, founders must proactively check the amount deposited is sufficient for recipients to claim and top up accordingly.

### 5. (Optional) Withdrawing Deposited Funds

The founder can immediately withdraw any unclaimed tokens by calling `withdrawDeposit`.

### 6. (Optional) Cancel an Unlocking Schedule

If the founder would like to immediately halt the progress of an unlocking schedule permanently, they can cancel the schedule by calling `cancel`. Any unlocked but unclaimed tokens are still available for the recipient to withdraw. If the founder created the unlocking schedule by accident and would like to wipe out the recipient's unclaimed claimable tokens, they can do so by setting `shouldWipeClaimableBalance: true`.

### 7. (Optional) Enable Delegate Claim

The founder can specify a wallet to act as the claiming delegate and trigger claiming for any recipient. The claimed tokens will go to the recipient's address. This is useful if the recipient is a cold wallet or cannot pay gas.

## Stakeholder Workflow

### 1. Adding FutureToken & TrackerToken to Wallet

By adding the project-specific FutureToken & TrackToken, the stakeholder can interact with their redemption NFT and view the current claimable token amount from the comfort of their wallet without having to visit TokenTable's website.

### 2. Claiming Unlocked Tokens

Stakeholders have the option to either claim through TokenTable's website or by calling `claim` on the block explorer.

### 3. (Optional) Claiming Unlocked Tokens from a Cancelled Schedule

If the stakeholder's unlocking schedule was canceled, they can claim any leftover unlocked tokens by calling `claim`.


# EVM


# APIs


# Core


# Unlocker

## ITokenTableUnlockerV2

*The lightweight interface for TokenTableUnlockerV2(.5.x), which handles token unlocking and distribution for TokenTable.*

### PresetCreated

```solidity
event PresetCreated(bytes32 presetId, uint256 batchId)
```

### ActualCreated

```solidity
event ActualCreated(bytes32 presetId, uint256 actualId, address recipient, uint256 recipientId, uint256 batchId)
```

### ActualCancelled

```solidity
event ActualCancelled(uint256 actualId, uint256 pendingAmountClaimable, bool didWipeClaimableBalance, uint256 batchId)
```

### TokensClaimed

```solidity
event TokensClaimed(uint256 actualId, address caller, address to, uint256 amount, uint256 feesCharged, uint256 batchId)
```

### TokensWithdrawn

```solidity
event TokensWithdrawn(address by, uint256 amount)
```

### ClaimingDelegateSet

```solidity
event ClaimingDelegateSet(address delegate, bool status)
```

### CreateDisabled

```solidity
event CreateDisabled()
```

### CancelDisabled

```solidity
event CancelDisabled()
```

### HookDisabled

```solidity
event HookDisabled()
```

### WithdrawDisabled

```solidity
event WithdrawDisabled()
```

### InvalidPresetFormat

```solidity
error InvalidPresetFormat()
```

*0x0ef8e8dc*

### PresetExists

```solidity
error PresetExists()
```

*0x7cbb15b4*

### PresetDoesNotExist

```solidity
error PresetDoesNotExist()
```

*0xbd88ff7b*

### ActualDoesNotExist

```solidity
error ActualDoesNotExist()
```

*0x06aed316*

### InvalidSkipAmount

```solidity
error InvalidSkipAmount()
```

*0x78c0fc43*

### NotPermissioned

```solidity
error NotPermissioned()
```

*0x7f63bd0f*

### initialize

```solidity
function initialize(address projectToken, address futureToken_, address deployer_, bool isCancelable_, bool isHookable_, bool isWithdrawable_) external virtual
```

*This contract should be deployed with `TTUDeployerLite`, which calls this function with the correct parameters.*

#### Parameters

| Name             | Type    | Description                                                                                                               |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| projectToken     | address | The address of the token that the founder intends to unlock and distribute.                                               |
| futureToken\_    | address | The address of the associated FutureToken.                                                                                |
| deployer\_       | address | The address of the deployer. It helps call the fee collector during claim.                                                |
| isCancelable\_   | bool    | If the founder is allowed to cancel schedules. Can be disabled later, but cannot be enabled again.                        |
| isHookable\_     | bool    | If the founder is allowed to attach external hooks to function calls. Can be disabled later, but cannot be enabled again. |
| isWithdrawable\_ | bool    | If the founder is allowed to withdraw deposited tokens. Can be disabled later, but cannot be enabled again.               |

### createPresets

```solidity
function createPresets(bytes32[] presetIds, struct Preset[] presets, uint256 batchId, bytes extraData) external virtual
```

Creates an unlocking schedule preset template.

*Emits `PresetCreated`. Only callable by the owner.*

#### Parameters

<table><thead><tr><th>Name</th><th width="196.66666666666666">Type</th><th>Description</th></tr></thead><tbody><tr><td>presetIds</td><td>bytes32[]</td><td>These IDs can be the hashes of a plaintext preset names but really there is no restriction. Will revert if they already exist.</td></tr><tr><td>presets</td><td>struct Preset[]</td><td>An array of <code>Preset</code> structs.</td></tr><tr><td>batchId</td><td>uint256</td><td>Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution.</td></tr><tr><td>extraData</td><td>bytes</td><td>An ERC-5750 parameter that's passed to the hook directly.</td></tr></tbody></table>

### createActuals

```solidity
function createActuals(address[] recipients, struct Actual[] actuals, uint256[] recipientIds, uint256 batchId, bytes extraData) external virtual
```

Creates an actual unlocking schedule based on a preset.

*Emits `ActualCreated`. A FutureToken is minted in the process with `tokenId == actualId`.*

#### Parameters

| Name         | Type             | Description                                                                                                                                  |
| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| recipients   | address\[]       | An array of token recipients for the schedules. Note that claiming eligibility can be modified by transfering the corresponding FutureToken. |
| actuals      | struct Actual\[] | An array of `Actual` structs.                                                                                                                |
| recipientIds | uint256\[]       | Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution.                                   |
| batchId      | uint256          | Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution.                                   |
| extraData    | bytes            | An ERC-5750 parameter that's passed to the hook directly.                                                                                    |

### withdrawDeposit

```solidity
function withdrawDeposit(uint256 amount, bytes extraData) external virtual
```

Withdraws existing deposit from the contract.

*Emits `TokensWithdrawn`. Only callable by the owner.*

#### Parameters

| Name      | Type    | Description                                               |
| --------- | ------- | --------------------------------------------------------- |
| amount    | uint256 | Amount of deposited funds the founder wishes to withdraw. |
| extraData | bytes   | An ERC-5750 parameter that's passed to the hook directly. |

### claim

```solidity
function claim(uint256[] actualIds, address[] claimTos, uint256 batchId, bytes extraData) external virtual
```

Claims claimable tokens for the specified schedules to the specified addresses respectively.

*Emits `TokensClaimed`. Only callable by the FutureToken owner.*

#### Parameters

| Name      | Type       | Description                                                                                                                                                                     |
| --------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| actualIds | uint256\[] | The IDs of the unlocking schedules that we are trying to claim from.                                                                                                            |
| claimTos  | address\[] | If we want to send the claimed tokens to an address other than the caller. To send the claimed tokens to the caller (default behavior), pass in `ethers.constants.AddressZero`. |
| batchId   | uint256    | Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution.                                                                      |
| extraData | bytes      | An ERC-5750 parameter that's passed to the hook directly.                                                                                                                       |

### delegateClaim

```solidity
function delegateClaim(uint256[] actualIds, uint256 batchId, bytes extraData) external virtual
```

Claims claimable tokens for the specified schedules on behalf of recipients. Claimed tokens are sent to the schedule recipients.

*Emits `TokensClaimed`. Only callable by the claiming delegate.*

#### Parameters

| Name      | Type       | Description                                                                                                |
| --------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
| actualIds | uint256\[] | The IDs of the unlocking schedules that we are trying to claim from on behalf of the recipients.           |
| batchId   | uint256    | Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution. |
| extraData | bytes      | An ERC-5750 parameter that's passed to the hook directly.                                                  |

### cancel

```solidity
function cancel(uint256[] actualIds, bool[] shouldWipeClaimableBalance, uint256 batchId, bytes extraData) external virtual returns (uint256[] pendingAmountClaimables)
```

Cancels an array of unlocking schedules effective immediately. Tokens not yet claimed but are already unlocked will be tallied.

*Emits `ActualCancelled`. Only callable by the owner.*

#### Parameters

| Name                       | Type       | Description                                                                                                                                                                        |
| -------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| actualIds                  | uint256\[] | The ID of the actual unlocking schedule that we want to cancel.                                                                                                                    |
| shouldWipeClaimableBalance | bool\[]    | If the unlocked and claimable balance of the canceled schedule should be wiped. This is usually used to delete an erroneously created schedule that has already started unlocking. |
| batchId                    | uint256    | Emitted as an event reserved for EthSign frontend use. This parameter has no effect on contract execution.                                                                         |
| extraData                  | bytes      | An ERC-5750 parameter that's passed to the hook directly.                                                                                                                          |

#### Return Values

| Name                    | Type       | Description                                                                                         |
| ----------------------- | ---------- | --------------------------------------------------------------------------------------------------- |
| pendingAmountClaimables | uint256\[] | Number of tokens eligible to be claimed by the affected stakeholders at the moment of cancellation. |

### setHook

```solidity
function setHook(contract ITTHook hook) external virtual
```

Sets the hook contract.

*Only callable by the owner.*

#### Parameters

| Name | Type             | Description                                 |
| ---- | ---------------- | ------------------------------------------- |
| hook | contract ITTHook | The address of the `ITTHook` hook contract. |

### setClaimingDelegate

```solidity
function setClaimingDelegate(address delegate, bool status) external virtual
```

Sets the claiming delegate who can trigger claims on behalf of recipients.

*Only callable by the owner.*

#### Parameters

| Name     | Type    | Description                                            |
| -------- | ------- | ------------------------------------------------------ |
| delegate | address | The claiming delegate we wish to set.                  |
| status   | bool    | Whether the delegate is added(true) or revoked(false). |

### disableCreate

```solidity
function disableCreate() external virtual
```

Permanently disables the `createActuals()` function.

*Only callable by the owner.*

### disableCancel

```solidity
function disableCancel() external virtual
```

Permanently disables the `cancel()` function.

*Only callable by the owner.*

### disableHook

```solidity
function disableHook() external virtual
```

Permanently disables the hook.

*Only callable by the owner.*

### disableWithdraw

```solidity
function disableWithdraw() external virtual
```

Permanently prevents the founder from withdrawing deposits.

*Only callable by the owner.*

### deployer

```solidity
function deployer() external view virtual returns (contract ITTUDeployer)
```

#### Return Values

| Name | Type                  | Description                                          |
| ---- | --------------------- | ---------------------------------------------------- |
| \[0] | contract ITTUDeployer | The deployer instance associated with this Unlocker. |

### futureToken

```solidity
function futureToken() external view virtual returns (contract ITTFutureTokenV2)
```

#### Return Values

| Name | Type                      | Description                                             |
| ---- | ------------------------- | ------------------------------------------------------- |
| \[0] | contract ITTFutureTokenV2 | The FutureToken instance associated with this Unlocker. |

### hook

```solidity
function hook() external view virtual returns (contract ITTHook)
```

#### Return Values

| Name | Type             | Description                                      |
| ---- | ---------------- | ------------------------------------------------ |
| \[0] | contract ITTHook | The external hook associated with this Unlocker. |

### claimingDelegates

```solidity
function claimingDelegates() external view virtual returns (address[])
```

#### Return Values

| Name | Type       | Description                                                                                      |
| ---- | ---------- | ------------------------------------------------------------------------------------------------ |
| \[0] | address\[] | Returns the array of claiming delegates who can trigger claims on behalf of schedule recipients. |

### isCreateable

```solidity
function isCreateable() external view virtual returns (bool)
```

#### Return Values

| Name | Type | Description                                        |
| ---- | ---- | -------------------------------------------------- |
| \[0] | bool | If the founder is allowed to create new schedules. |

### isCancelable

```solidity
function isCancelable() external view virtual returns (bool)
```

#### Return Values

| Name | Type | Description                                    |
| ---- | ---- | ---------------------------------------------- |
| \[0] | bool | If the founder is allowed to cancel schedules. |

### isHookable

```solidity
function isHookable() external view virtual returns (bool)
```

#### Return Values

| Name | Type | Description                                                 |
| ---- | ---- | ----------------------------------------------------------- |
| \[0] | bool | If the founder can attach external hooks to function calls. |

### isWithdrawable

```solidity
function isWithdrawable() external view virtual returns (bool)
```

#### Return Values

| Name | Type | Description                                                 |
| ---- | ---- | ----------------------------------------------------------- |
| \[0] | bool | If the founder can withdraw deposited but unclaimed tokens. |

### pendingAmountClaimableForCancelledActuals

```solidity
function pendingAmountClaimableForCancelledActuals(uint256 actualId) external view virtual returns (uint256)
```

#### Parameters

| Name     | Type    | Description               |
| -------- | ------- | ------------------------- |
| actualId | uint256 | The canceled schedule ID. |

#### Return Values

| Name | Type    | Description                                                                                            |
| ---- | ------- | ------------------------------------------------------------------------------------------------------ |
| \[0] | uint256 | The amount of tokens from canceled schedules that have been unlocked but unclaimed by the stakeholder. |

### getEncodedPreset

```solidity
function getEncodedPreset(bytes32 presetId) external view virtual returns (bytes)
```

To decode in JS, use:

```js
ethers.utils.defaultAbiCoder.decode(
  ["uint256[]", "uint256", "uint256[]", "uint256[]", "bool"],
  encodedPreset
);
```

#### Parameters

| Name     | Type    | Description                                 |
| -------- | ------- | ------------------------------------------- |
| presetId | bytes32 | The ID of the preset we are trying to read. |

#### Return Values

| Name | Type  | Description                                                                         |
| ---- | ----- | ----------------------------------------------------------------------------------- |
| \[0] | bytes | An ABI-encoded `Preset`, as nested objects cannot be returned directly in Solidity. |

### actuals

```solidity
function actuals(uint256 actualId) external view virtual returns (struct Actual)
```

Returns the Actual struct based on the input ID.

### BIPS\_PRECISION

```solidity
function BIPS_PRECISION() external pure virtual returns (uint256)
```

#### Return Values

| Name | Type    | Description                                 |
| ---- | ------- | ------------------------------------------- |
| \[0] | uint256 | The basis point precision of this Unlocker. |

### calculateAmountClaimable

```solidity
function calculateAmountClaimable(uint256 actualId) public view virtual returns (uint256 deltaAmountClaimable, uint256 updatedAmountClaimed)
```

Calculates the amount of unlocked tokens that have yet to be claimed in an actual unlocking schedule.

*This is the most complex part of the smart contract. Quite a bit of calculations are performed here.*

#### Parameters

| Name     | Type    | Description                                                       |
| -------- | ------- | ----------------------------------------------------------------- |
| actualId | uint256 | The ID of the actual unlocking schedule that we are working with. |

#### Return Values

| Name                 | Type    | Description                                                                                                      |
| -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| deltaAmountClaimable | uint256 | Amount of tokens claimable right now.                                                                            |
| updatedAmountClaimed | uint256 | New total amount of tokens claimed. This is the sum of all previously claimed tokens and `deltaAmountClaimable`. |

### simulateAmountClaimable

```solidity
function simulateAmountClaimable(uint256 actualId, uint256 claimTimestampAbsolute) public view virtual returns (uint256 deltaAmountClaimable, uint256 updatedAmountClaimed)
```

Simulates the amount of unlocked tokens that have yet to be claimed at a specific time in an actual unlocking schedule.

*This is the most complex part of the smart contract. Quite a bit of calculations are performed here.*

#### Parameters

| Name                   | Type    | Description                                                       |
| ---------------------- | ------- | ----------------------------------------------------------------- |
| actualId               | uint256 | The ID of the actual unlocking schedule that we are working with. |
| claimTimestampAbsolute | uint256 | The simulated time of claim.                                      |

#### Return Values

| Name                 | Type    | Description                                                                                                      |
| -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| deltaAmountClaimable | uint256 | Amount of tokens claimable right now.                                                                            |
| updatedAmountClaimed | uint256 | New total amount of tokens claimed. This is the sum of all previously claimed tokens and `deltaAmountClaimable`. |


# Data Models

Data models present in the unlocker smart contract.

## Preset

A `Preset` is an unlocking schedule template that contains information that's shared across all stakeholders within a single round.

In this system, cliff unlocks are considered linear as well. This enables us to mix and match cliffs and linears at will, providing full customizability. Cliff waiting periods have a linear basis point of 0 and cliff unlocking moments have a duration of 1 second.

Note that all relative timestamps are relative to the absolute start timestamp. Absolute timestamps are standard UNIX epoch timestamps in seconds.

`linearStartTimestampsRelative`: An array of start timestamps for each linear segment. `linearEndTimestampRelative`: The timestamp that marks the end of the final linear segment. `linearBips`: The basis point that is unlocked for each linear segment. Must add up to `TokenTableUnlockerV2.BIPS_PRECISION()`. `numOfUnlocksForEachLinear`: The number of unlocks within each respective linear segment. `stream`: If the tokens should unlock as a stream instead of a cliff at the end of the linear segment subdivision.

```solidity
struct Preset {
  uint256[] linearStartTimestampsRelative;
  uint256 linearEndTimestampRelative;
  uint256[] linearBips;
  uint256[] numOfUnlocksForEachLinear;
  bool stream;
}
```

## Actual

An `Actual` is an actual unlocking schedule for a single stakeholder and builds on top of an existing preset. An actual contains information that is different from one stakeholder to the next.

`presetId`: The ID of the `Preset` that this `Actual` references. `startTimestampAbsolute`: The timestamp of when this unlocking schedule starts. `amountClaimed`: The amount of tokens that have already been claimed by the recipient. `totalAmount`: The maximum amount of tokens that the recipient can claim throughout the entire schedule.

```solidity
struct Actual {
  bytes32 presetId;
  uint256 startTimestampAbsolute;
  uint256 amountClaimed;
  uint256 totalAmount;
}
```


# FutureToken

## ITTFutureTokenV2

*The lightweight interface for TTFutureTokenV2(.5.x), which handles unlocking schedule ownership for TokenTable.*

### DidSetBaseURI

```solidity
event DidSetBaseURI(string newURI)
```

### NotPermissioned

```solidity
error NotPermissioned()
```

*0x7f63bd0f*

### initialize

```solidity
function initialize(address projectToken, bool isTransferable) external
```

*This contract should be deployed with `TTUDeployerLite`, which calls this function with the correct parameters.*

#### Parameters

| Name           | Type    | Description                                                                 |
| -------------- | ------- | --------------------------------------------------------------------------- |
| projectToken   | address | The address of the token that the founder intends to unlock and distribute. |
| isTransferable | bool    | If the FutureTokens (aka schedules) can be transfered once minted.          |

### setAuthorizedMinterSingleUse

```solidity
function setAuthorizedMinterSingleUse(address authorizedMinter_) external
```

This contract should be deployed with `TTUDeployerLite`, which calls this function with the correct parameters.

*This function can only be called once.*

#### Parameters

| Name               | Type    | Description                                                                                                          |
| ------------------ | ------- | -------------------------------------------------------------------------------------------------------------------- |
| authorizedMinter\_ | address | The address which is authorized to mint new FutureTokens. This is set to the corresponding Unlocker in the deployer. |

### safeMint

```solidity
function safeMint(address to) external returns (uint256 tokenId)
```

Safely mints a new FutureToken to the specified address.

*This function can only be called by the authorized minter.*

#### Parameters

| Name | Type    | Description                           |
| ---- | ------- | ------------------------------------- |
| to   | address | The recipient of the new FutureToken. |

#### Return Values

| Name    | Type    | Description                                         |
| ------- | ------- | --------------------------------------------------- |
| tokenId | uint256 | The minted token ID (aka actual ID or schedule ID). |

### setURI

```solidity
function setURI(string uri) external
```

Updates the base URI.

*This function can only be called by the owner of the authorized minter, which is usually the founder.*

#### Parameters

| Name | Type   | Description       |
| ---- | ------ | ----------------- |
| uri  | string | The new base URI. |

### getClaimInfo

```solidity
function getClaimInfo(uint256 tokenId) external view returns (uint256 deltaAmountClaimable, uint256 amountAlreadyClaimed, bool isCancelable)
```

Gets information regarding the unlocking schedule associated with this FutureToken.

#### Parameters

| Name    | Type    | Description                   |
| ------- | ------- | ----------------------------- |
| tokenId | uint256 | The actual ID or schedule ID. |

#### Return Values

| Name                 | Type    | Description                                                                                               |
| -------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
| deltaAmountClaimable | uint256 | The amount of unlocked and unclaimed funds currently eligible to be claimed by the owner of the given ID. |
| amountAlreadyClaimed | uint256 | The amount of unlocked and claimed funds of the given ID.                                                 |
| isCancelable         | bool    | If the schedule associated with this ID can be canceled by the founder.                                   |


# TrackerToken

## ITTTrackerTokenV2

### initialize

```solidity
function initialize(address ttuInstance_) external
```

*This contract should be deployed with `TTUDeployerLite`, which calls this function with the correct parameters.*

#### Parameters

| Name          | Type    | Description                                |
| ------------- | ------- | ------------------------------------------ |
| ttuInstance\_ | address | The address of the corresponding Unlocker. |


# Utilities


# Deployer

## ITTUDeployer

*This is the deployer for all TokenTable core and proxy contracts. All initial setup and configuration is automatically done here. To save gas and enable easy upgradeability, all deployed contracts are `Clone` or `BeaconProxy` instances. You should avoid deploying TokenTable contracts individually unless you know what you're doing.*

### TTUDeployerInitialized

```solidity
event TTUDeployerInitialized(address unlockerImpl, address futureTokenImpl, address trackerTokenImpl, address beaconManagerImpl, address feeCollector)
```

### TokenTableSuiteDeployed

```solidity
event TokenTableSuiteDeployed(address by, string projectId, address unlocker, address futureToken, address trackerToken)
```

### FeeCollectorChanged

```solidity
event FeeCollectorChanged(address feeCollector)
```

### AlreadyDeployed

```solidity
error AlreadyDeployed()
```

*0xa6ef0ba1*

### feeCollector

```solidity
function feeCollector() external returns (contract ITTUFeeCollector)
```

*Exposes the fee collector variable.*

#### Return Values

| Name | Type                      | Description                       |
| ---- | ------------------------- | --------------------------------- |
| \[0] | contract ITTUFeeCollector | An instance of the fee collector. |

### deployTTSuite

```solidity
function deployTTSuite(address projectToken, string projectId, bool isUpgradeable, bool isTransferable, bool isCancelable, bool isHookable, bool isWithdrawable) external returns (contract ITokenTableUnlockerV2, contract ITTFutureTokenV2, contract ITTTrackerTokenV2)
```

Deploys and configures a new set of TokenTable products.

*Emits `TokenTableSuiteDeployed`. Throws: `AlreadyDeployed`.*

#### Parameters

| Name           | Type    | Description                                                                                          |
| -------------- | ------- | ---------------------------------------------------------------------------------------------------- |
| projectToken   | address | The project token address.                                                                           |
| projectId      | string  | A unique projectId, otherwise it will revert.                                                        |
| isUpgradeable  | bool    | When set to false, a `Clone` instead of a `BeaconProxy` is created to prevent future upgradeability. |
| isTransferable | bool    | Allow FutureToken to be transferable.                                                                |
| isCancelable   | bool    | Allow unlocking schedules to be cancelled in the Unlocker.                                           |
| isHookable     | bool    | Allow Unlocker to call an external hook.                                                             |
| isWithdrawable | bool    | Allow the founder to withdraw deposited funds.                                                       |


# External Hook

## ITTHook

### didCall

```solidity
function didCall(bytes originalMsgData, address originalMsgSender) external
```

Forwards the call context from the hooked contract.

*Reverts within hooks will revert the hooked contract as well.*

#### Parameters

| Name              | Type    | Description                                  |
| ----------------- | ------- | -------------------------------------------- |
| originalMsgData   | bytes   | Forwarded calldata from the called function. |
| originalMsgSender | address | Forwarded sender from the called function.   |


# Fee Collector

## ITTUFeeCollector

*This contract handles TokenTable service fee calculation.*

### DefaultFeeSet

```solidity
event DefaultFeeSet(uint256 bips)
```

### CustomFeeSet

```solidity
event CustomFeeSet(address unlockerAddress, uint256 bips)
```

### getFee

```solidity
function getFee(address unlockerAddress, uint256 tokenTransferred) external view returns (uint256 tokensCollected)
```

Returns the amount of fees to collect.

#### Parameters

| Name             | Type    | Description                                         |
| ---------------- | ------- | --------------------------------------------------- |
| unlockerAddress  | address | The address of the Unlocker. Used to fetch pricing. |
| tokenTransferred | uint256 | The number of tokens transferred.                   |

#### Return Values

| Name            | Type    | Description                              |
| --------------- | ------- | ---------------------------------------- |
| tokensCollected | uint256 | The number of tokens to collect as fees. |


# Versionable

## IVersionable

*This interface is implemented by all major TokenTable contracts to keep track of their versioning for upgrade compatibility checks.*

### version

```solidity
function version() external pure returns (string)
```


# SDK

You can integrate TokenTable into your smart contract by importing our [NPM package](https://www.npmjs.com/package/@ethsign/tokentable-evm-contracts) that includes all Solidity contracts.


# Changelog

#### 2.7.0 (5/11/2025, a215d3f)

* Added an emergency withdraw functionality that allows project owners to withdraw permanently deposited tokens using a pseudo-multisig mechanism with Team TokenTable
* Added default fee and fee token in FeeCollector
* Fixed a validation bug for presets in Unlocker
* Fixed an issue with delegate.xyz
* Fixed issues in TrackerToken
* Fixed fee calculation logic when the claim amount is 0
* Updated CustomERC2771Context's base class
* Optimized precision calculation in `Unlocker::simulateAmountClaimable`
* Replaced `FutureToken::getClaimInfo`

#### 2.6.1 (3/3/2025, 71cc316)

* Added TTUMulticallDeployer contract for batch deployment functionality
* Fixed bug in deployer that forced FutureTokens to be untransferable when the instance was deployed as a clone

#### 2.6.0 (12/27/2024, 4b6f605)

* Added support for external NFT as FutureToken (experimental)
* Added delegate.xyz support to manage delegate claims
* Added distinctive versioning to all extensions
* Added ETH support in FeeCollector
* Fixed issues in the subgraph that would lead to a crash
* Fixed issues in the deployer
* Fixed an issue in Unlocker that prevented upgrades
* Removed deprecated and mock source files

#### 2.5.7 (2/14/2024, 1422afd)

* Fixed various edge case findings from the Nethermind audit of Cairo TokenTable.

#### 2.5.6 (1/26/2024, 3aede9e)

* Optimized `ITTHook` interface.
* Added `isCreateable` to fix a loophole that allowed founders to drain the pool even when `isCancelable` and `isWithdrawable` are false.
* Allow multiple claim delegates instead of just one.

#### 2.5.5 (12/29/2023, a112d20)

* Refactored storage to be upgrade-safe namespaced.
* Fixed an issue that allowed recipients to bypass skipped amounts.
* Revised use of `PresetDoesNotExist` error.
* Revised selector syntax to improve consistency.
* Optimized various variable initialization.
* Added support for using native tokens with the Unlocker.

#### 2.5.4 (12/26/2023, 28f1a51)

* Added `bytes memory extraData` to all functions.

#### 2.5.3 (12/22/2023, cad7f0e)

* `simulateAmountClaimable` now returns 0 instead of reverting under certain conditions.
* `calculateAmountClaimable` now checks if the `actualId` that's passed in exists.
* Fixed instances where `msg.data` instead of `_msgData()` is used.

#### 2.5.2 (12/21/2023, f5ed369)

* Added `recipientIds` to `createActuals` function and `ActualCreated` event.

#### 2.5.1 (12/20/2023, 373e52a)

* Updated `ActualCreated` event.

#### 2.5.0 (12/19/2023, b7a0171)

* Switched to a shared token pool for all Actuals instead of an individual pool for each Actual.
* Fixed an issue when calculating claimables.
* Added an option for the founder to perform a complete cancellation.
* Added delegate claim.
* Added `simulateAmountClaimable`.


# Solana

### Introduction

TokenTable Unlocker provides users with a secure, self-custodial token management and unlocking experience. Once initialized, Unlocker is fully independent from us, with each token unlock managed solely by the project owner.

## 🚀 Quick Start

### What is TokenTable Unlocker?

TokenTable Unlocker is our most independent and feature-rich token distributor. Targeted specifically for fine-tuned and complex token unlocking onchain, Unlocker sets itself apart with the following features: customizable unlocking schedules, partial deposits, unruggable configurations, delegate claiming, streamlined gas efficiency, and a comprehensive dashboard to help build the ideal unlocking schedule. Unlocker splits token distributions into presets, which outline an unlocking schedule for a group of recipients, and actuals, which define necessary claim-related variables, such as the amount of tokens a recipient is allocated.

### Key Benefits

* ✅ **Fully Independent** - Initialized unlockers are independently managed by project owners
* ✅ **Customizable Unlocking Schedules** - Linear unlocks, cliff unlocks, and flexible pauses
* ✅ **Partial Deposits** - A full deposit is not required upfront
* ✅ **Unruggable Standard** - Token unlocks can be configured for no-cancel and no-withdrawal restrictions
* ✅ **Claiming Tokens on Behalf of Recipients** - Delegate users can send unlocked tokens directly to recipients' wallets
* ✅ **Comprehensive Dashboard** - Easily create, view, and track all relevant unlocker information from a personalized dashboard

## 📋 System Overview

### Account Structure

<figure><img src="https://2778259896-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FPwpN13H2gt6vlHSZcOAi%2Fuploads%2FDb9dVG2lJqb36peUQU2H%2FUntitled%20diagram%20_%20Mermaid%20Chart-2025-07-10-012547.png?alt=media&amp;token=e07323fc-b2b2-4a92-8117-51de92a2d478" alt=""><figcaption></figcaption></figure>

### Storage Pattern

A contract has one config storage account, which stores the contract admin's account address and the default fee collector's deployment address. This account will also store a new admin's account address for secure two-step ownership transfers.

```rust
pub struct Config {
  pub admin: Pubkey,
  pub new_admin: Pubkey,
  pub default_fee_collector: Pubkey,
}
```

The TokenTable Unlocker program can manage multiple token unlocks. Each initialized unlocker uses the following storage pattern:

```rust
pub struct Unlocker {
  pub owner: Pubkey,
  pub project_token: Pubkey,
  pub fee_collector: Pubkey,
  pub fee_token: Pubkey,
  pub is_createable: bool,
  pub is_cancelable: bool,
  pub is_transferable: bool,
  pub is_withdrawable: bool,
  #[max_len(20)]
  pub project_id: String,
}
```

### Claim Mechanism

There are two ways to claim tokens:

1. **Direct Claim** - Recipients claim their own tokens
2. **Delegate Claim** - Authorized delegate claims for recipients

```rust
pub fn claim(
  ctx: Context<Claim>,
  project_id: String,
  actual_id: u64,
  batch_id: u64
 ) -> Result<()>
```

### **Security Features**

* Reentrancy protection on all claim functions
* Time-based token allocation calculations
* Owner-only administrative functions

## 💰 Fee System

### Overview

The fee system provides flexible fee collection for claims.

### Components

1. **Fee Collector** - External `FeeCollector` program that calculates and manages vault accounts
2. **Fee Token** - Native or SPL token for fees
3. **Vault Accounts** - Program Derived Accounts (PDAs) for holding fee tokens

## 🔒 Security

### Access Control

| Role           | Permissions                                                           |
| -------------- | --------------------------------------------------------------------- |
| Program Admin  | Set fee collector, transfer program admin                             |
| Unlocker Owner | Set unlocker parameters, withdraw tokens, transfer unlocker ownership |
| Delegate       | Claim on behalf of recipients                                         |
| Users          | Claim their allocated tokens                                          |

### Security Features

* **Reentrancy Guards** - All state-changing functions protected
* **Time Windows** - Enforced distribution periods

## 📡 Events & Errors

### Events

| Event                    | Description                                          |
| ------------------------ | ---------------------------------------------------- |
| `Initialized`            | Successful unlocker initialization                   |
| `ActualCreated`          | Successful actual creation                           |
| `PresetCreated`          | Successful preset creation                           |
| `ClaimingDelegateSet`    | Delegate account permissions changed                 |
| `TokensClaimed`          | Tokens claimed successfully                          |
| `ActualCancelled`        | Successfully cancelled an actual                     |
| `TokensWithdrawn`        | Successfully withdrew tokens                         |
| `CreateDisabled`         | Successfully disabled creating actuals               |
| `CancelDisabled`         | Successfully disabled cancel functionality           |
| `TransferActualDisabled` | Successfully disabled actual transfer functionality  |
| `WithdrawDisabled`       | Successfully disabled token withdrawal functionality |

### Errors

| Error                 | Description                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| `ActualDoesNotExist`  | Actual does not exist                                                      |
| `PresetDoesNotExist`  | Actual does not reference a valid preset                                   |
| `DataMismatch`        | Call parameters do not match supplied accounts in transaction              |
| `InvalidPresetFormat` | Preset data contains inconsistencies or an invalid ID                      |
| `InvalidSkipAmount`   | An actual's `amount_claimed` must be less than the actual's `total_amount` |
| `NotOwner`            | Not unlocker owner                                                         |
| `NotProgramAdmin`     | Not program admin                                                          |
| `NotTransferable`     | Unlocker does not allow the transfer of actuals                            |
| `NotWithdrawable`     | Unlocker does not allow the withdrawal of deposited tokens                 |
| `NotPermissioned`     | Missing permissions for user or unlocker feature disabled                  |
| `InvalidFeeCollector` | Provided fee collector account is incorrect                                |


# Starknet

The Cairo version of TokenTable was officially released in February 2024 on Starknet. It has feature and API parity with the EVM version aside from the following differences:

* Functions and variables are named with `snake_case` instead of `camelCase`.
* TrackerToken is not implemented.

You can find the source code [here](https://github.com/EthSign/tokentable-v2-starknet). A formal audit was conducted by [Nethermind](https://github.com/NethermindEth/PublicAuditReports/blob/main/NM0163-FINAL_TOKENTABLE.pdf).


# FAQ

Common questions and answers about TokenTable.

* **How much does TokenTable cost?**\
  Please contact us for pricing details.
* **How is TokenTable different from other token cap table management platforms?**\
  TokenTable fulfills the end-to-end lifecycle for investors, founders, and team members, including planning, fundraising, fund transfer, claiming, and token management. Unlike other platforms, we streamlined the token and investment management workflow so no matter what your role or fundraising stage is, you will find TokenTable to be the one-stop-shop for your project and investments. Some of our unique capabilities include integration with EthSign’s on-chain Signatures, which creates legally binding PDF contracts built on blockchain technologies, on-platform fund transfer, multisig support, and smart templates, with more capabilities coming soon.
* **How is TokenTable data stored?**\
  Sign uses organizational and technical safeguards to maintain the integrity and security of the information we collect. Centralized data storage is secured by AWS KMS with various access control mechanisms such as required MFA for all sessions and periodic review of information security and access logs. Decentralized data storage is fully encrypted using threshold cryptography and only accessible to the involved parties, and not to even the Sign team. Please be aware that no security measures are perfect or impenetrable and thus we cannot and do not guarantee the security of your data. It is important that you maintain the security and control of your account credentials and do not share your password or private key with anyone.&#x20;
* **How does TokenTable protect my data privacy?**\
  Sign only collects data necessary to fulfill our services and comply with local laws and regulations. We do not collect tracking information, nor do we sell your information to any third-party advertisers. We perform periodic database pruning to remove data marked as inactive. You can also request a copy of your data or delete your data.&#x20;
* **Legal & Compliance?**\
  TokenTable is based on legal contracts uploaded by users. The terms executed on-chain are dependent on the stipulations input by users. We are just an execution layer for legal instruments.\
  Signatures made using EthSign technology are legally binding in jurisdictions where technology-neutral laws are in effect. This includes the United States, China, Australia, New Zealand, the Cayman Islands, and the British Virgin Islands. Users outside of these jurisdictions are responsible for doing their own research to determine whether contracts signed using EthSign technology will be considered legally binding in their jurisdiction.&#x20;

#### Reach out with any further questions to <support@tokentable.xyz>.


# Feedback and Troubleshooting

Get the help you need and provide valuable feedback to help us improve your experience with TokenTable.

Respond to the following form to provide feedback to us.

{% embed url="<https://form.typeform.com/to/PQrhy6MM>" %}


