airdrop 0.1.0

Mint and Airdrop Framework on Solana for Sovereign Individuals
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# Mint-and-Airdrop Framework

A flexible, production-ready Solana program for creating SPL tokens and managing airdrop campaigns with multiple allocation modes and fee structures.

## Overview

The Mint-and-Airdrop Framework provides a comprehensive solution for token creation and distribution on Solana. It supports multiple campaign management strategies, flexible fee structures, and various allocation methods (on-chain accounts, off-chain signatures, and merkle proofs).

## Key Features

### **Flexible Token Minting**
- Create SPL tokens with Metaplex metadata support
- Custom mint addresses via PDA derivation with noise
- Automatic mint authority revocation for immutable supply caps
- Mint-specific treasury for token storage

### **Multiple Allocation Modes**
- **On-chain Allocation**: Creates allocation accounts for each recipient (via `Allocate` instruction)
- **Off-chain Record**: Claims against off-chain database records (via `Claim` Method 1, no `Allocate` needed)
- **Merkle Mode**: Efficient merkle tree-based claims for large campaigns (via `Claim` Method 2, no `Allocate` needed)

### **Flexible Fee Structure**
- Configurable fees per operation type
- Fair pricing based on actual costs
- Transparent fee tracking and withdrawal

### **Multiple Distribution Methods**
- On-chain allocation claims
- Off-chain signature-based claims
- Merkle proof-based claims
- Direct batch transfers (campaign owner)

### **Security & Best Practices**
- PDA-based treasury (no admin co-signing required)
- Program-controlled fee account
- Admin authority management
- Comprehensive account validation

## Architecture

### Core Components

```
Program Config (Singleton PDA)
├── Program Admin Authority
├── Fee Configuration
│   ├── Mint Fee (lamports)
│   ├── Allocation Fee Per Recipient (lamports)
│   ├── Merkle Fee Per Recipient (lamports)
│   └── Direct Transfer Fee Per Recipient (lamports)
├── Fee Account (PDA)
└── Initialization Status

Campaign Account (Per Campaign PDA)
├── Campaign Owner
├── Mint Address
├── Treasury Token Account
├── Merkle Root (optional)
└── Campaign Status

Allocation Account (Mode 0 Only)
├── Recipient
├── Amount
└── Claim Status
```

## Instructions

### 1. Initialize
Initialize the program with admin and fee configuration.

```rust
pub struct Initialize {
    pub admin: Pubkey,
    pub mint_fee_lamports: [u8; 8],
    pub allocation_fee_per_recipient_lamports: [u8; 8],
    pub merkle_fee_per_recipient_lamports: [u8; 8],
    pub direct_transfer_fee_per_recipient_lamports: [u8; 8],
}
```

**Accounts:**
- Initializer (signer)
- Config PDA
- Fee Account PDA
- System Program
- Rent Sysvar

### 2. CreateMint
Create a new SPL token mint with metadata. All tokens up to `max_supply` are minted upfront to the mint treasury, and mint authority is revoked.

```rust
pub struct CreateMint {
    pub mint_id: [u8; 32],
    pub noise: [u8; 4],           // For address grinding
    pub mint: Pubkey,              // Optional: use provided or derive PDA
    pub name: [u8; 32],
    pub symbol: [u8; 10],
    pub uri: [u8; 128],            // Metaplex metadata URI
    pub decimals: u8,
    pub max_supply: [u8; 8],      // Must be > 0
}
```

**Fee:** `mint_fee_lamports` (recommended: 0.1 SOL)

### 3. CreateCampaign
Create a new campaign using an existing mint. Multiple campaigns can share the same mint.

```rust
pub struct CreateCampaign {
    pub campaign_id: [u8; 32],
    pub mint: Pubkey,
    pub merkle_root: [u8; 32],     // Zero = no merkle mode
    pub recipient_count: [u8; 8],  // Required if merkle_root != 0
    pub initial_supply: [u8; 8],   // Transfer from mint treasury
    pub max_supply: [u8; 8],
}
```

**Fee:** `merkle_fee_per_recipient_lamports × recipient_count` (if merkle_root set)

### 4. Allocate
Allocate tokens to recipients by creating on-chain allocation accounts.

**Mode:** On-chain only (creates Allocation accounts for each recipient)
- **Fee:** `allocation_fee_per_recipient_lamports × count`

**Note:** Off-chain allocations are handled via `Claim` Method 1 (record mode) without a prior `Allocate` call. Merkle-based claims use `Claim` Method 2 (merkle mode) without `Allocate`.

```rust
pub struct Allocate {
    pub campaign_id: [u8; 32],
    pub count: u8,
    // Variable: Vec<(Pubkey recipient, u64 amount)>
}
```

### 5. Claim
Claim allocated tokens. Supports three methods:

- **Method 0 (allocation):** Claim against on-chain Allocation account (requires prior `Allocate` call)
- **Method 1 (record):** Claim against off-chain database record (with signature, no `Allocate` needed)
- **Method 2 (merkle):** Claim against merkle proof (requires merkle root in campaign, no `Allocate` needed)

```rust
pub struct Claim {
    pub campaign_id: [u8; 32],
    pub method: u8,                // 0=allocation, 1=record, 2=merkle
    pub amount: [u8; 8],
    pub merkle_proof_length: u8,
    // Variable: merkle proof data or signature
}
```

**Fee:** None (recipient pays transaction fee)

### 6. DirectTransfer
Campaign owner transfers tokens directly to recipients in batches (no claim step required).

```rust
pub struct DirectTransfer {
    pub campaign_id: [u8; 32],
    pub count: u8,                 // 1-10 recipients per batch
    // Variable: Vec<(Pubkey recipient, u64 amount)>
}
```

**Fee:** `direct_transfer_fee_per_recipient_lamports × count`

**Batch Size:** Limited to 10 recipients per transaction

### 7. Replenish
Transfer additional tokens from mint treasury to campaign treasury.

```rust
pub struct Replenish {
    pub campaign_id: [u8; 32],
    pub amount: [u8; 8],
}
```

**Fee:** None

### 8. MintTokens
Mint additional tokens to campaign treasury (if mint authority not revoked).

```rust
pub struct MintTokens {
    pub campaign_id: [u8; 32],
    pub amount: [u8; 8],
}
```

**Fee:** None

### 9. UpdateConfig
Update fee rates and admin authority (admin-only).

```rust
pub struct UpdateConfig {
    pub new_admin: Pubkey,                      // Pubkey::default() = keep current
    pub mint_fee_lamports: [u8; 8],             // u64::MAX = keep current
    pub allocation_fee_per_recipient_lamports: [u8; 8],
    pub merkle_fee_per_recipient_lamports: [u8; 8],
    pub direct_transfer_fee_per_recipient_lamports: [u8; 8],
}
```

### 10. WithdrawFees
Withdraw accumulated fees to beneficiary (admin-only).

```rust
pub struct WithdrawFees {
    pub amount: [u8; 8],  // 0 = withdraw all
}
```

## Fee Structure

### Recommended Default Fees

| Fee Type | Amount | Description |
|----------|--------|-------------|
| Mint Fee | 0.1 SOL (100M lamports) | Per mint creation |
| Allocation Fee | 0.001 SOL (1M lamports) | Per recipient (Mode 0 only) |
| Merkle Fee | 0.0001 SOL (100K lamports) | Per recipient (when merkle root set) |
| Direct Transfer Fee | 0.0005 SOL (500K lamports) | Per recipient (batch transfers) |

### Fee Collection Points

- **Mint Fee:** Collected during `CreateMint`
- **Allocation Fee:** Collected during `Allocate` (on-chain allocation accounts)
- **Merkle Fee:** Collected during `CreateCampaign` (if merkle root set)
- **Direct Transfer Fee:** Collected during `DirectTransfer`
- **Off-chain Record Claims:** No fees (recipient pays transaction fee only)

## Account Structure

### Config Account
```rust
pub struct Config {
    pub admin: Pubkey,
    pub mint_fee_lamports: u64,
    pub allocation_fee_per_recipient_lamports: u64,
    pub merkle_fee_per_recipient_lamports: u64,
    pub direct_transfer_fee_per_recipient_lamports: u64,
    pub fee_account: Pubkey,
    pub total_fees_collected: u64,
    pub updated_at: i64,
}
```

### Campaign Account
```rust
pub struct Campaign {
    pub owner: Pubkey,
    pub mint: Pubkey,
    pub treasury: Pubkey,           // Campaign treasury token account
    pub merkle_root: [u8; 32],      // Zero = no merkle mode
    pub status: u8,
    pub total_allocated: u64,
    pub total_claimed: u64,
    pub max_supply: u64,
    pub created_at: i64,
}
```

### Allocation Account (Mode 0 Only)
```rust
pub struct Allocation {
    pub campaign: Pubkey,
    pub recipient: Pubkey,
    pub amount: u64,
    pub claimed: bool,
    pub allocated_at: i64,
    pub claimed_at: i64,
}
```

## Usage Examples

### Complete Campaign Lifecycle

```rust
use airdrop_api::sdk;

// 1. Initialize program (one-time setup)
let initialize_ix = sdk::initialize(
    &initializer,
    admin,
    100_000_000,      // mint_fee: 0.1 SOL
    1_000_000,        // allocation_fee: 0.001 SOL per recipient
    100_000,          // merkle_fee: 0.0001 SOL per recipient
    500_000,          // direct_transfer_fee: 0.0005 SOL per recipient
)?;

// 2. Create mint
let mint_id = [0u8; 32];
let noise = [0u8; 4];
let create_mint_ix = sdk::create_mint(
    &payer,
    mint_id,
    noise,
    Pubkey::default(), // Use PDA derivation
    b"My Token",
    b"MTK",
    b"https://example.com/metadata.json",
    9,
    1_000_000_000,     // max_supply
)?;

// 3. Create campaign (with merkle root)
let campaign_id = [1u8; 32];
let merkle_root = compute_merkle_root(&recipients);
let create_campaign_ix = sdk::create_campaign(
    &payer,
    &campaign_owner,
    campaign_id,
    mint_address,
    merkle_root,
    1000,              // recipient_count
    100_000_000,       // initial_supply
    1_000_000_000,     // max_supply
)?;

// 4. Allocate tokens (Mode 0 - on-chain)
let allocate_ix = sdk::allocate_onchain(
    &campaign_owner,
    campaign_id,
    vec![
        (recipient1, 1000),
        (recipient2, 2000),
        // ... more recipients
    ],
)?;

// 5. Claim tokens (Method 0 - on-chain)
let claim_ix = sdk::claim_onchain(
    &recipient,
    campaign_id,
)?;

// 6. Direct transfer (batch)
let direct_transfer_ix = sdk::direct_transfer(
    &campaign_owner,
    campaign_id,
    vec![
        (recipient1, 500),
        (recipient2, 500),
        // ... up to 10 recipients
    ],
)?;
```

### Merkle Claim Example

```rust
// Generate merkle proof off-chain
let merkle_proof = generate_merkle_proof(&recipient, &amount, &merkle_tree)?;

// Claim using merkle proof
let claim_ix = sdk::claim_merkle(
    &recipient,
    campaign_id,
    amount,
    merkle_proof.path,
    merkle_proof.indices,
)?;
```

## Project Structure

```
.
├── api/                    # API crate (types, SDK, PDA functions)
│   ├── src/
│   │   ├── lib.rs         # Main exports
│   │   ├── consts.rs      # Constants
│   │   ├── error.rs       # Error types
│   │   ├── instruction.rs # Instruction definitions
│   │   ├── event.rs       # Event definitions
│   │   ├── sdk.rs         # SDK helper functions
│   │   ├── pda.rs         # PDA derivation functions
│   │   ├── merkle.rs      # Merkle proof utilities
│   │   ├── loaders.rs     # Account validation traits
│   │   └── state/         # State structs
│   │       ├── config.rs
│   │       ├── campaign.rs
│   │       └── allocation.rs
│   └── Cargo.toml
├── program/                # Program crate (instruction handlers)
│   ├── src/
│   │   ├── lib.rs         # Entrypoint and dispatch
│   │   ├── initialize.rs
│   │   ├── create_mint.rs
│   │   ├── create_campaign.rs
│   │   ├── allocate.rs
│   │   ├── claim.rs
│   │   ├── direct_transfer.rs
│   │   ├── replenish.rs
│   │   ├── mint_tokens.rs
│   │   ├── update_config.rs
│   │   └── withdraw_fees.rs
│   └── Cargo.toml
├── docs/                   # Design documentation
├── Cargo.toml              # Workspace configuration
└── README.md
```

## Setup & Installation

### Prerequisites

- Rust 1.70+ (with rust-toolchain specified)
- Solana CLI tools
- Anchor CLI (optional, for testing)

### Build

```bash
# Build all crates
cargo build

# Build program specifically
cargo build-sbf --manifest-path program/Cargo.toml

# Run tests
cargo test
```

### Deployment

```bash
# Deploy to devnet
solana program deploy target/deploy/airdrop_program.so --program-id <PROGRAM_ID>

# Set program ID in code
# Update api/src/lib.rs with actual program ID
```

## Design Philosophy

**"Simple Is Best, Yet Elegant"**

- **Simple**: Clear fee structure, straightforward operations
-**Elegant**: Follows Steel framework patterns, consistent codebase
-**Professional**: Thoughtful fee pricing and account management
-**Secure**: PDA-based accounts, comprehensive validation
-**Flexible**: Configurable fees, multiple allocation modes

## Security Features

1. **PDA Treasury**: No admin co-signing required for claims
2. **Account Validation**: Comprehensive checks using Steel's chainable API
3. **Fee Account**: Program-controlled PDA for secure fee storage
4. **Admin Verification**: All admin operations verify authority
5. **Mint Authority**: Can be revoked for immutable supply caps
6. **Merkle Verification**: Cryptographic proof validation for claims

## Reference Projects

This project follows patterns from:
- `miracle-copy`: Merkle tree implementation, account validation patterns
- `escrow-copy`: Fee account design, PDA treasury patterns
- `steel-master-copy`: Framework idioms and best practices

## License

Apache-2.0

## Contributing

Contributions welcome! Please follow the existing code patterns and Steel framework idioms.

## Status

✅ **Production Ready** - All core features implemented and tested

- ✅ Initialize program
- ✅ Create mint with metadata
- ✅ Create campaigns
- ✅ Allocate tokens (on-chain allocation accounts)
- ✅ Claim tokens (Methods 0, 1, 2)
- ✅ Direct transfer batches
- ✅ Fee collection and withdrawal
- ✅ Config management

For detailed design documentation, see the `docs/` directory.