# 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="/files/BKgPsumn3hhNHcdrZYjT" 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                                |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tokentable.xyz/for-developers/unlocker/solana.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
