Skill: azep-ninja/token-launch-pad (version 1.0.15)

--- name: token-launcher description: > Launch tokens and keep 100% of your creator fees. Direct Mode provides full SDK integration guides for Clanker (7 EVM chains), Flaunch (Base), and Pump.fun (Solana) — no middleman, no platform cut. Easy Mode offers a convenience path via the Tator API (90/10 split). Includes strategy evaluation, fee economics, claiming, recipient updates, and tax/legal guidance. Triggers: "token idea", "launch a coin", "launch a token", "deploy a token", "token strategy", "claim fees", "creator fees", "update fee recipient", "token launch on base", "launch on solana", "clanker", "flaunch", "pump.fun", "token economics", "is this a good token". metadata: openclaw: emoji: "🚀" requires: env: [] notes: > This skill requires no API keys or environment variables. Easy Mode uses the x402 payment protocol (HTTP 402), where authentication and payment are handled by the user's wallet at request time via a signed PAYMENT-SIGNATURE header — no stored credentials needed. Direct Mode implementation guides are in REFERENCE.md for developers building in their own infrastructure. publisher: Quick Intel / Web3 Collective homepage: https://quickintel.io source: https://github.com/Quick-Intel/openclaw-skills/tree/main/token-launcher documentation: https://docs.tator.bot --- # Token Launcher Launch tokens and keep your fees. This is an open-source skill that helps you launch tokens without giving away 30-50% of your creator earnings to launch platforms. --- ## The Problem Most token launch platforms take 30-50% of your creator fee earnings. Some charge upfront launch fees on top of that. Others layer in subscriptions, required platform tokens, or custodial wallets that hold your funds. You build the token, drive the volume, and someone else keeps the biggest slice. ## The Math A token doing $500K in trading volume on Clanker generates ~$5,000 in pool fees (1% of volume). Here's who keeps what: | Path | Your Share | Platform Cut | You Keep | |------|-----------|-------------|----------| | **Direct Mode (this skill)** | $5,000 | $0 | **$5,000 (100%)** | | **Easy Mode (Tator API)** | $4,500 | $500 (10%) | **$4,500 (90%)** | | **Typical launch platforms** | $2,500-3,500 | $1,500-2,500 (30-50%) | **$2,500-3,500** | The difference compounds. At $5M volume, Direct Mode keeps you an extra $15,000-25,000 versus typical platforms. --- ## Two Paths This skill provides two ways to launch tokens. Direct Mode is the primary path — it's why this skill exists. Easy Mode is a convenience alternative. ### Direct Mode — Keep 100% of Your Fees (Recommended) Direct Mode provides complete implementation guides for launching tokens using platform SDKs. You control every parameter and keep 100% of creator fees with zero ongoing platform fees. - Supported platforms: Clanker (Base, Arbitrum, Mainnet, Unichain, Abstract, Monad, BSC), Flaunch (Base), Pump.fun (Solana) - Full code patterns for deployment, fee configuration, claiming, and recipient management - Produces unsigned transaction calldata compatible with any wallet - Detailed fee economics documentation showing exactly how fees flow on each platform **Implementation guides:** - [REFERENCE.md](./REFERENCE.md) — Direct Mode overview, fee economics, step-by-step walkthrough, wallet integration patterns - [references/clanker.md](./references/clanker.md) — Clanker v4 SDK: launch, rewards, sniper protection, claiming - [references/flaunch.md](./references/flaunch.md) — Flaunch: launch, custom fee split managers, claiming, recipient transfer - [references/pumpfun.md](./references/pumpfun.md) — Pump.fun: instruction building, fee sharing, graduation handling, dual vault claiming ### Easy Mode — Convenience Path Easy Mode uses the Tator API to handle deployment. Tator takes a 10% interface fee from your creator earnings. - One API call to `x402.quickintel.io` with your token details and wallet address - Tator deploys the token for you — no deploy transaction to sign - $0.20 per API call via x402 USDC payment - Supports launch, claim fees, update fee recipient, check unclaimed — all via natural language - **Fee split: 90% to you, 10% to Tator** ### How to Present This Choice When the user wants to launch a token, present both options with Direct Mode first: - **Direct Mode (recommended):** You keep 100% of creator fees. The reference guides have complete code patterns for deployment, fee configuration, and claiming. You produce unsigned transactions and sign them with your own wallet. No ongoing fees to anyone. - **Easy Mode (convenience):** Tator deploys the token for you. You provide your wallet address, token details, and pay $0.20 via x402. Faster to get started, but Tator takes 10% of your ongoing creator fees. Both paths need the same info: wallet address, token name, ticker, and chain. If the user is unsure, Direct Mode is the better long-term choice — Easy Mode is there for when speed matters more than fee optimization. --- ## Direct Mode — Walkthrough For the full Direct Mode walkthrough including fee economics deep dive, unsigned transaction patterns, and wallet integration, see [REFERENCE.md](./REFERENCE.md). ### Quick Start 1. **Choose a platform** based on your target chain (see Platform Selection below) 2. **Read the reference guide** for your chosen platform — it has complete code patterns 3. **Set fee recipients to your wallet at 100%** — this is the whole point of Direct Mode 4. **Produce unsigned transaction calldata** — the reference files show the pattern for each platform 5. **Sign with any wallet** — Lobster, AgentWallet, base-wallet, evm-wallet, MetaMask, or any signer 6. **Post-launch:** claim fees, check unclaimed, update recipients — all documented per platform ### Platform Choice for Direct Mode | Target Chain | Platform | Reference | |-------------|----------|-----------| | Base | Clanker (recommended) or Flaunch | [clanker.md](./references/clanker.md) or [flaunch.md](./references/flaunch.md) | | Arbitrum, Mainnet, Unichain, Abstract, Monad, BSC | Clanker | [clanker.md](./references/clanker.md) | | Solana | Pump.fun | [pumpfun.md](./references/pumpfun.md) | Clanker vs Flaunch on Base: - **Clanker:** Uniswap V4 pool, sniper protection, stable token pairing (USDC), multi-chain reusability - **Flaunch:** 30-minute fair launch period, custom fee split managers, bonding curve model --- ## Platform Selection (Full Comparison) | Feature | Clanker | Flaunch | Pump.fun | |---------|---------|---------|----------| | **Chains** | Base, Arbitrum, Mainnet, Unichain, Abstract, Monad, BSC | Base | Solana | | **Pool type** | Uniswap V4 | Bonding curve → Uniswap | Bonding curve → Raydium | | **Swap fee** | 1.2% (1% pool + 0.2% protocol) | Configurable | Variable (pre/post graduation) | | **Creator fee share** | Configurable via reward recipients | Configurable via fee split manager | Configurable via sharing config | | **Pairing token** | WETH default, USDC/USDT available | ETH | SOL | | **Sniper protection** | Yes (decaying fee — 66.7% → 4.2% over 15s) | Fair launch period (default 30 min) | Bonding curve mechanics | | **Graduation** | N/A (immediate Uniswap pool) | N/A | Yes (graduates to Raydium at threshold) | | **Token standard** | ERC-20 | ERC-20 | SPL (Token-2022) | | **Supply** | 1 billion (fixed) | 100 billion (configurable) | Standard pump.fun supply | | **Creation fee** | Free | Free | Minimal SOL for rent | ### When to Use What **Clanker** — Your default for EVM launches. Widest chain support (7 chains), Uniswap V4 pools with built-in sniper protection, stable token pairing option. Best liquidity depth and DEX integration. **Flaunch** — When you want a fair launch period on Base. The 30-minute fair launch window prevents snipers by design. Custom fee split manager gives you fine-grained control over fee distribution. **Pump.fun** — For Solana launches. Bonding curve model means the token graduates to Raydium once it hits market cap threshold. Strong Solana ecosystem visibility — tokens show up in pump.fun's discovery feed. --- ## Evaluating a Concept Before deploying anything, figure out what you actually have. Not every idea needs a token, and not every token needs to launch today. ### The Launch Stack Every token that sustains attention beyond the first day has four layers working together. This isn't a scorecard — it's a diagnostic tool. **Layer 1: The Hook** — The thing that makes someone stop scrolling. The name, the visual, the one-liner. Say the name out loud. Does it land instantly or need explanation? If it takes more than one sentence to explain why this exists, the hook isn't sharp enough. **Layer 2: The Engine** — The reason fees keep flowing after launch day. Types of engines: cultural (ongoing conversation), product (funds something useful), mechanic (burns, airdrops, staking), social (tied to a growing community). If there's no engine, the token will spike on launch and bleed. **Layer 3: The Story** — The narrative that justifies increasing price. Strong stories: "this token funds [specific thing] and every holder is backing it." Weak stories: "it's a community token" (what community? why this token?). **Layer 4: The Moat** — What makes this token hard to replicate. First-mover, builder credibility, integrated product, community lock-in, or technical integration. If there's no moat, launch fast — speed itself is a moat. ### Search Before You Judge Never evaluate a concept in a vacuum. Before giving your take, search for existing tokens with similar names/narratives, cultural context, comparable launches, and the builder's blind spots. --- ## ⚠️ Before You Launch: Tax & Legal Reality Check **Token deployment is irreversible. Creator fees are income. Most platforms skip this section entirely.** ### When This Matters Most Launching a meme token for fun? The tax implications are relatively straightforward — mostly capital gains if and when you sell. But the moment your token becomes **"more than just a meme"** — ongoing creator fees, product funded by fee income, regular token launches — **that's when the tax and legal implications get serious, and you need professional guidance before you launch.** ### What Every Builder Should Know **This is general information, not tax or legal advice. Tax treatment varies by jurisdiction. Consult a qualified professional.** - **Creator fee income is likely taxable income** — In most jurisdictions, ongoing fees are treated as income, not capital gains. - **Every transaction can be a taxable event** — Selling tokens, swapping, receiving fee payments, distributing airdrops. - **You owe taxes when you receive income, not when you cash out** — Earn $50K in fees, reinvest it all, token crashes — you likely still owe taxes on the $50K. - **Record-keeping starts at launch** — Track: creation date, every fee payment (with fiat value at receipt), every sale/swap, gas fees. - **Regulatory enforcement is increasing globally** — Governments are investing in blockchain analytics. ### Jurisdiction Overview **🇺🇸 United States** — Digital assets treated as property (IRS Notice 2014-21). Creator fee income likely ordinary income. Starting 2025: Form 1099-DA reporting. **🇬🇧 United Kingdom** — HMRC treats crypto as property. Income from token fees is income tax. CGT allowance currently £3,000. **🇩🇪 Germany** — Crypto held over 1 year is tax-free on disposal. Under 1 year: income tax rates up to 45%. **🇦🇺 Australia** — ATO treats crypto as property. 50% CGT discount for holdings over 12 months. Fee income assessable at fair market value. **🇸🇬 Singapore** — No capital gains tax for individuals (current rules). Business income from token activities may be taxable. **🇦🇪 UAE** — Currently no federal income tax on individuals. Framework developing under VARA. **🇨🇦 Canada** — CRA treats crypto as a commodity. 50% capital gains inclusion rate. Business income fully taxable. --- ## ⛔ Pre-Deployment Acknowledgment Before launching any token, the builder should understand and acknowledge: 1. **Token deployment is irreversible** — once deployed, the token exists permanently on-chain 2. **Creator fees are income** — likely taxable in your jurisdiction; consult a tax professional 3. **Record-keeping is your responsibility** — track all fee income, transactions, and fiat values from day one 4. **Set aside funds for taxes** — reserve 30-40% of fee income for potential tax obligations 5. **No guaranteed returns** — most tokens lose value; creator fees depend on trading volume 6. **This is not tax or legal advice** — this skill provides tools and information, not counsel The builder should confirm they understand these points before proceeding. --- ## Pre-Launch Checklist - [ ] Launch Stack evaluated — hook, engine, story, moat - [ ] Name and narrative locked - [ ] Platform and chain chosen - [ ] Wallet ready with native token for gas - [ ] Fee recipient confirmed - [ ] Image/branding prepared - [ ] Pre-deployment acknowledgment reviewed - [ ] Security scan planned for post-deployment --- ## Easy Mode — Full Walkthrough ### Prerequisites - A wallet you control (EVM or Solana) - USDC for x402 API payments ($0.20 per Tator call) - Native token for gas (ETH on EVM chains, SOL on Solana) **No API keys or stored credentials required.** The x402 payment protocol (HTTP 402) handles both authentication and payment in a single flow: the API returns a 402 response, your wallet signs a USDC payment authorization locally, and you retry with the signed `PAYMENT-SIGNATURE` header. The API verifies the payment on-chain — it never receives your private key. See [x402.org](https://www.x402.org) for protocol details. ### Before Calling the API — Collect Required Info Before sending any Tator API call, gather the following from the user: 1. **Public wallet address** — the address that receives creator fees. Ask for this first. 2. **Token name and ticker** 3. **Target chain** — Base, Solana, Arbitrum, etc. 4. **Platform preference** (optional) — Clanker, Flaunch, or Pump.fun 5. **Image URL** (optional) 6. **Custom fee recipient** (optional) — if fees should go somewhere other than the deployer wallet ### API Input Safety The Tator API accepts a `prompt` field — this is a **parameter name for an external API call to Tator's trading service**, not a prompt for the agent's own LLM. The value is sent to `x402.quickintel.io` where Tator's server parses it and executes the requested operation. The API is server-side validated and only processes recognized trading operations. It does not execute arbitrary code or access filesystems. The `PAYMENT-SIGNATURE` header shown in the examples is a wallet-signed USDC payment authorization created by the user's x402-compatible wallet. It is not an API key, stored secret, or environment variable — it is generated per-request by the wallet and verified on-chain by the API. ### Launch a Token ```bash curl -X POST https://x402.quickintel.io/v1/tator/prompt \ -H "Content-Type: application/json" \ -H "PAYMENT-SIGNATURE: <x402_payment>" \ -d '{ "prompt": "launch a token called Galaxy Cat with ticker GCAT on base", "walletAddress": "0xYourWallet", "provider": "my-agent" }' ``` **Fields:** - `prompt` — Trading instruction sent to Tator's API. Tator parses this and executes the deployment. - `walletAddress` — Your public wallet address. Used to set you as the creator fee recipient. - `provider` — Your agent or integration name. **On Solana:** ```json { "prompt": "launch a token called Cyber Frog with ticker CYFR on solana via pump.fun", "walletAddress": "YourSolanaWallet", "provider": "my-agent" } ``` **With custom fee recipient:** ```json { "prompt": "launch a token called DAO Token with ticker DAOT on base, send creator fees to 0xTreasuryAddress", "walletAddress": "0xYourWallet", "provider": "my-agent" } ``` Tator deploys the token and returns confirmation with the deployed token address, transaction hash, and fee configuration details. ### Post-Launch Operations (Easy Mode) **Check unclaimed fees:** ```json { "prompt": "check my unclaimed fees for token 0xTokenAddress on base", "walletAddress": "0xYourWallet", "provider": "my-agent" } ``` **Claim creator fees:** ```json { "prompt": "claim my creator fees for token 0xTokenAddress on base", "walletAddress": "0xYourWallet", "provider": "my-agent" } ``` **Update fee recipient:** ```json { "prompt": "update the fee recipient for token 0xTokenAddress on base to 0xNewRecipientAddress", "walletAddress": "0xYourWallet", "provider": "my-agent" } ``` ### Security Scan Post-Launch Use Quick Intel ($0.03 per scan) to verify your deployed token: Call `POST https://x402.quickintel.io/v1/scan/full` with `{"chain": "base", "tokenAddress": "0xYourDeployedToken"}` to check for honeypot flags, tax irregularities, or scanner false positives. ### Discovery Call `GET https://x402.quickintel.io/accepted` to get supported payment networks, pricing, and schemas. --- ## Post-Launch 1. **Run a Quick Intel scan** — Verify the token looks clean before promoting 2. **Monitor fee accumulation** — Check unclaimed fees regularly 3. **Claim on a cadence** — Periodic claims for better tax tracking 4. **Watch reinvestment risk** — Taxes are owed on income regardless of reinvestment 5. **Revisit the Launch Stack** — If volume drops, diagnose which layer is failing --- ## File Structure | File | What's Inside | |------|--------------| | **SKILL.md** (this file) | Strategy, platform selection, concept evaluation, tax/legal, Easy Mode API walkthrough | | **[REFERENCE.md](./REFERENCE.md)** | Direct Mode overview, fee economics deep dive, step-by-step walkthrough, wallet integration, shared code patterns | | **[references/clanker.md](./references/clanker.md)** | Clanker v4 SDK — launch, claim, update recipient, sniper config | | **[references/flaunch.md](./references/flaunch.md)** | Flaunch — launch, fee split manager, claim, transfer share | | **[references/pumpfun.md](./references/pumpfun.md)** | Pump.fun — manual instructions, fee sharing, graduation handling | --- ## What This Skill Is NOT - **Not tax advice.** General information provided for awareness. Consult a professional. - **Not legal advice.** Token launches may have securities law implications. - **Not a guarantee of profits.** Most tokens lose value. - **Not a "get rich quick" tool.** This skill helps builders launch responsibly and keep more of what they earn.

path: REFERENCE.md
content: # Token Launcher — Direct Mode Reference

> **This file is developer documentation only.** It contains code examples and integration patterns for developers to implement in their own codebase. This skill does not execute any code, does not access environment variables, and does not handle private keys at runtime. All code samples below are reference material — they run in your infrastructure, not in this skill.

Direct Mode means integrating with Clanker, Flaunch, or Pump.fun directly from your own application code. You keep 100% of creator fees — no interface cut, no platform overhead.

**Source:** https://github.com/Quick-Intel/openclaw-skills/tree/main/token-launcher
**Publisher:** Quick Intel / Web3 Collective — https://quickintel.io

---

## ⚠️ Security First

The code examples in this reference show how to sign transactions in your own infrastructure. This skill itself never signs transactions, never accesses your private key, and never executes blockchain operations. Your application code does — and you control that code.

Before implementing in your own codebase:

1. **Create a dedicated launch wallet** — never reuse your main wallet or a wallet holding significant funds
2. **Use a secrets manager** — load private keys from AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, or equivalent. Never hardcode keys or store them in plaintext `.env` files in production
3. **Test with minimal funds** — fund your launch wallet with only what's needed for gas. On Base, ~0.01 ETH is sufficient. On Solana, ~0.05 SOL
4. **Verify contract addresses** — before calling any contract, verify the address matches the official deployment. Check Basescan, Arbiscan, or Solscan
5. **Audit third-party SDKs** — Clanker SDK, Flaunch contracts, and Pump.fun programs are maintained by their respective teams, not by Quick Intel. Review their repos before trusting them with real value

Code examples in this reference and the per-platform guides use placeholder variable names like `PRIVATE_KEY` and `RPC_URL`. These represent values that should be loaded securely at runtime — they are never sent to any external service.

---

## What You Need

| Requirement | Why | Security Note |
|-------------|-----|---------------|
| **RPC endpoint** | Talk to the blockchain | Use authenticated endpoints from Alchemy, QuickNode, or Helius. Not sensitive but keep private to avoid rate limit abuse |
| **Signing wallet** | Sign transactions your agent creates | **Dedicated launch wallet only.** Store key in a secrets manager, never in plaintext. Fund with minimal gas |
| **SDK / libraries** | Clanker SDK, viem/ethers for EVM, @solana/web3.js for Solana | Verify package integrity. Pin versions in package.json |
| **IPFS upload** | For token metadata (name, image, description) | API keys for Pinata/web3.storage are sensitive — store securely |
| **Native token for gas** | ETH on EVM chains, SOL on Solana | Only fund what's needed. ~0.01 ETH on Base, ~0.05 SOL on Solana |

---

## Fee Economics Deep Dive

Understanding how fees flow on each platform is critical. This is where you make your money.

### Clanker Fee Structure

Every swap on a Clanker token incurs a **1.2% total fee**:

```
Swap amount: $1,000
├── 1.0% pool fee ($10.00)
│   ├── Reward recipient 1: configurable BPS (e.g., 100% to you)
│   ├── Reward recipient 2: configurable BPS (e.g., 0% — or split with a partner)
│   └── You define up to N recipients with basis points totaling 10,000
└── 0.2% Clanker protocol fee ($2.00) — goes to Clanker, you can't change this
```

**In Direct Mode, you set the reward recipients.** You can send 100% of the 1% pool fee to yourself. Or split it across multiple wallets (a treasury, a dev fund, a co-creator). The 0.2% Clanker protocol fee always goes to Clanker — that's the cost of using their infrastructure.

**In Easy Mode (Tator API),** the split is automatically set to 90% you / 10% Tator. Tator's 10% comes out of the 1% pool fee — not on top of it.

**Reward configuration:**
- `recipients`: Array of `{ recipient, admin, bps, token }` — who gets paid, who can update, basis points (out of 10,000), which tokens ("Both" for pool fee token + paired token)
- `sniperFees`: Decaying fee to punish bots — starts high (66.7%), decays to normal (4.2%) over configurable seconds
- `pool.pairedToken`: WETH by default, or a stablecoin (USDC on Base/Arb/Mainnet, USDT on BSC)

### Flaunch Fee Structure

Flaunch uses an `AddressFeeSplitManager` contract to handle fee distribution:

```
Token swap fee
└── creatorFeeAllocation (set at launch, e.g., 100%)
    └── AddressFeeSplitManager
        ├── Recipient 1: 90% (your wallet)
        └── Recipient 2: 10% (another wallet, or 0% if you want 100%)
```

**In Direct Mode,** you deploy a custom fee split manager with whatever split you want. Set `recipientShares` to `[{ your_wallet, 100_00000 }]` for 100% to yourself. The basis points are in `share * 100000` format (so 90% = `90_00000`, 100% = `100_00000`).

**Key difference from Clanker:** Flaunch's fee manager is a separate deployed contract. You create it via `TreasuryManagerFactory.deployAndInitializeManager()` before launching the token, then pass it as the `treasuryManagerParams.manager` in the launch call.

### Pump.fun Fee Structure

Pump.fun uses a `SharingConfig` PDA to define fee splits:

```
Pre-graduation (bonding curve):
  Swap fees → Creator Vault (native SOL) → distributed to shareholders

Post-graduation (Raydium AMM):
  Swap fees → AMM Coin Creator Vault (WSOL)
  ↓ (transfer_creator_fees_to_pump instruction)
  → Creator Vault (native SOL)
  ↓ (distribute_creator_fees instruction)
  → Shareholders per sharing config BPS
```

**In Direct Mode,** you set up the sharing config with your own split. The fee sharing requires two instructions: `create_fee_sharing_config` (creates the config PDA) then `update_fee_shares` (sets the shareholders and their BPS). Total BPS must equal 10,000.

**Graduation matters:** Once a Pump.fun token hits the bonding curve threshold and migrates to Raydium, fees accumulate as WSOL in the AMM vault instead of native SOL in the pump vault. To claim post-graduation, you need to first run `transfer_creator_fees_to_pump` (unwraps WSOL → SOL into pump vault) then `distribute_creator_fees` (pays out shareholders). Both can be in one transaction.

---

## The 100% vs 90/10 Decision

| Factor | Direct Mode (100%) | Easy Mode (90/10) |
|--------|-------------------|-------------------|
| **Setup time** | Hours to days | Minutes |
| **Code required** | SDK integration, wallet management, RPC | One HTTP call |
| **Maintenance** | You handle upgrades, errors, RPC issues | Tator handles it |
| **Fee claiming** | You build the claim logic | One prompt |
| **Multi-chain** | You integrate each chain separately | One API, all chains |
| **Cost per launch** | Gas only | $0.20 API + gas |
| **Creator fee share** | 100% of pool fees | 90% of pool fees |
| **Best for** | High-volume launchers, custom integrations | Most agents and developers |

**Rule of thumb:** If you're launching 1-2 tokens, Easy Mode saves you days of work for a 10% fee that's negligible on small volume. If you're launching dozens of tokens or driving millions in volume, Direct Mode's 10% savings compounds into real money.

---

## Shared Patterns

### IPFS Metadata Upload

All three platforms need token metadata uploaded to IPFS. The metadata JSON:

```json
{
  "name": "Galaxy Cat",
  "symbol": "GCAT",
  "description": "Galaxy Cat ($GCAT) - The first feline in the cosmos",
  "image": "ipfs://QmYourImageHash",
  "external_link": ""
}
```

Upload the JSON to IPFS and use the returned URI as the token's `tokenUri` (Clanker/Flaunch) or `metadataUri` (Pump.fun). Services like Pinata, web3.storage, or Infura IPFS all work.

### Unsigned Transaction Pattern (EVM)

For agent wallets that sign externally (OpenClaw, Lobster.cash, Coinbase Agentic), you need to return unsigned transactions rather than executing directly. Both Clanker and Flaunch support this:

```typescript
// Get the transaction config from the SDK
const txConfig = await sdk.getSomeTransaction({ ... });

// Encode the calldata
const data = encodeFunctionData({
  abi: txConfig.abi,
  functionName: txConfig.functionName,
  args: txConfig.args,
});

// Return unsigned transaction for the agent to sign
return {
  to: txConfig.address,
  data: data,
  value: (txConfig.value || 0).toString(),
  gas: '300000',
};
```

Pump.fun on Solana works differently — it uses a bot wallet that signs and submits directly, because Solana transactions require all signers present at signing time. See [references/pumpfun.md](./references/pumpfun.md) for details.

### Fee Recipient Configuration

All three platforms let you set who receives creator fees at launch time. All three also let you update the recipient after launch:

| Platform | Set at Launch | Update After Launch | Who Can Update |
|----------|--------------|-------------------|----------------|
| Clanker | `rewards.recipients[].recipient` | `updateRewardRecipient()` | The `admin` of that reward entry |
| Flaunch | `recipientShares[].recipient` in the fee manager | `transferRecipientShare()` | The current recipient |
| Pump.fun | Shareholders in `update_fee_shares` | `update_fee_shares` with new shareholders | The config admin (bot wallet) |

### Security Scanning

After deploying via any platform, run a Quick Intel scan to verify the contract looks clean from an external perspective:

```bash
curl -X POST https://x402.quickintel.io/v1/scan/full \
  -H "Content-Type: application/json" \
  -H "PAYMENT-SIGNATURE: <x402_payment>" \
  -d '{"chain": "base", "tokenAddress": "0xYourDeployedToken"}'
```

This catches false positives that might scare off buyers (even legit tokens sometimes trip scanner heuristics) and verifies the deployed contract behaves as expected.

---

## Platform Implementation Guides

Each platform has its own reference file with full implementation details:

| Platform | Chains | Guide |
|----------|--------|-------|
| **Clanker v4** | Base, Arbitrum, Mainnet, Unichain, Abstract, Monad, BSC | [references/clanker.md](./references/clanker.md) |
| **Flaunch** | Base | [references/flaunch.md](./references/flaunch.md) |
| **Pump.fun** | Solana | [references/pumpfun.md](./references/pumpfun.md) |

---

## Direct Mode Step-by-Step

### Step 1: Choose a Platform

| Target Chain | Platform | Reference File |
|-------------|----------|---------------|
| Base | Clanker (recommended) or Flaunch | [references/clanker.md](./references/clanker.md) or [references/flaunch.md](./references/flaunch.md) |
| Arbitrum, Mainnet, Unichain, Abstract, Monad, BSC | Clanker | [references/clanker.md](./references/clanker.md) |
| Solana | Pump.fun | [references/pumpfun.md](./references/pumpfun.md) |

Clanker vs Flaunch on Base:
- **Clanker:** Uniswap V4 pool, sniper protection, stable token pairing (USDC), multi-chain support
- **Flaunch:** 30-minute fair launch period, custom fee split managers, bonding curve model

### Step 2: Walk Through the Implementation

Use the platform reference file to understand each piece:

1. **Setup** — imports, client initialization, wallet configuration. See platform's official docs for secure key loading.
2. **Fee configuration** — set 100% of fees to your wallet. This is why you chose Direct Mode.
3. **Launch function** — deployment code with token name, symbol, metadata, fee recipients, platform-specific options.
4. **Fee management** — claim, check unclaimed, update recipient patterns for post-launch.
5. **Unsigned transaction output** — for EVM (Clanker, Flaunch), use `encodeFunctionData` to produce calldata for any wallet. For Solana (Pump.fun), see bot wallet signing pattern.

### Wallet Integration

For EVM launches, the reference files show patterns for producing unsigned transaction objects:

```json
{
  "to": "0xContractAddress",
  "data": "0xEncodedCalldata",
  "value": "0",
  "chainId": 8453,
  "gas": "500000"
}
```

Compatible with any wallet: Lobster, AgentWallet, base-wallet, evm-wallet, MetaMask, or any signer that accepts raw transaction objects.

For Solana (Pump.fun), the signing model is different — see [references/pumpfun.md](./references/pumpfun.md).

### Step 3: Configure Fee Recipients (100% to You)

**Clanker:** `rewards.recipients` with your wallet at `bps: 10_000` (100%).

**Flaunch:** Deploy `AddressFeeSplitManager` with your wallet at `share: 100_00000` (100%).

**Pump.fun:** `create_fee_sharing_config` + `update_fee_shares` with your wallet at `10000` BPS (100%).

### Step 4: Test Before Going Live

- Deploy with a fresh wallet holding minimal funds (gas only)
- Verify with a Quick Intel scan
- Confirm fee recipients on block explorer
- Test the claim flow

### Step 5: Post-Launch Fee Management

| Operation | Reference Section |
|-----------|------------------|
| Check unclaimed fees | Platform reference → "Check Unclaimed" |
| Claim creator fees | Platform reference → "Claim" |
| Update fee recipient | Platform reference → "Update Recipient" |
| Verify token security | Quick Intel scan |


path: references/clanker.md
content: # Clanker v4 — Direct Mode Reference

> **This file is developer documentation only.** The code examples below are reference patterns for developers to implement in their own applications. This skill does not execute code or access private keys at runtime.

Launch ERC-20 tokens with Uniswap V4 pools on 7 EVM chains. Built-in sniper protection, configurable reward splits, and stable token pairing.

**Publisher:** Quick Intel / Web3 Collective — https://quickintel.io
**Source:** https://github.com/Quick-Intel/openclaw-skills/tree/main/token-launcher
**Clanker SDK docs:** https://clanker.gitbook.io/clanker-documentation/sdk/v4.0.0

---

## Setup

```bash
npm install clanker-sdk viem
```

```typescript
import { Clanker } from "clanker-sdk/v4";
import {
  FEE_CONFIGS,
  POOL_POSITIONS,
  WETH_ADDRESSES,
} from "clanker-sdk";

// Initialize Clanker with your viem clients
// See viem docs for wallet/account setup: https://viem.sh/docs/clients/wallet
const clanker = new Clanker({
  publicClient,  // viem publicClient for your target chain
  wallet: walletClient,  // viem walletClient with your signing account
});
```

> **Wallet setup:** Use viem's `createPublicClient` and `createWalletClient` to initialize your clients. For wallet account setup (loading keys securely), see the [viem documentation](https://viem.sh/docs/accounts/local). Use a dedicated launch wallet — see the [Security section in REFERENCE.md](../REFERENCE.md).

---

## Supported Chains

| Chain | Chain ID | WETH Address | Stable Pairing |
|-------|----------|-------------|----------------|
| Base | 8453 | Via `WETH_ADDRESSES` from SDK | USDC: [REDACTED_SECRET] |
| Arbitrum | 42161 | Via SDK | USDC: [REDACTED_SECRET] |
| Mainnet | 1 | Via SDK | USDC: [REDACTED_SECRET] |
| Unichain | 130 | Via SDK | — |
| Abstract | — | Via SDK | — |
| Monad | — | Via SDK | — |
| BSC | 56 | WBNB via SDK | USDT: BSC USDT address |

Import chain objects from `viem/chains` (`base`, `arbitrum`, `mainnet`, `unichain`). For Monad and Abstract, define custom chain configs.

---

## Launch a Token

### 100% to You (Direct Mode)

```typescript
const { txHash, waitForTransaction, error } = await clanker.deploy({
  chainId: 8453, // Base
  name: "Galaxy Cat",
  symbol: "GCAT",
  image: "https://example.com/gcat.png", // or ipfs:// URI
  tokenAdmin: YOUR_WALLET_ADDRESS,
  metadata: {
    description: "Galaxy Cat ($GCAT) - The first feline in the cosmos",
  },
  pool: {
    pairedToken: WETH_ADDRESSES[8453], // WETH on Base
    positions: POOL_POSITIONS.Standard,
  },
  fees: FEE_CONFIGS.StaticBasic,
  rewards: {
    recipients: [
      {
        recipient: YOUR_WALLET_ADDRESS,  // 100% to you
        admin: YOUR_WALLET_ADDRESS,       // you control this reward entry
        bps: 10_000,                      // 100% of pool fees
        token: "Both",                    // receive both token types
      },
    ],
  },
  sniperFees: {
    startingFee: 666_777,   // 66.7% starting fee
    endingFee: 41_673,      // 4.2% ending fee
    secondsToDecay: 15,     // Decays over 15 seconds
  },
  vanity: true, // Try to get a vanity address
});

if (error) {
  console.error("Deploy failed:", error.message);
} else {
  console.log("TX:", txHash);
  const { address } = await waitForTransaction();
  console.log("Token deployed at:", address);
}
```

### Split Between Multiple Recipients

```typescript
rewards: {
  recipients: [
    {
      recipient: CREATOR_WALLET,
      admin: CREATOR_WALLET,
      bps: 7_000,           // 70%
      token: "Both",
    },
    {
      recipient: TREASURY_WALLET,
      admin: CREATOR_WALLET, // creator can update treasury's share
      bps: 3_000,           // 30%
      token: "Both",
    },
  ],
},
```

Total BPS across all recipients must equal 10,000.

### Stable Token Pairing (USDC/USDT)

Instead of pairing with WETH, pair with a stablecoin:

```typescript
pool: {
  pairedToken: [REDACTED_SECRET], // USDC on Base
  positions: POOL_POSITIONS.Standard,
},
```

Not all chains have stable pairing available yet. Check `STABLE_PAIR_ADDRESSES` for your target chain.

---

## Sniper Protection

Clanker v4 includes decaying fee sniper protection. When a token launches, the first swaps pay an elevated fee that decays to normal over a configurable window:

```typescript
sniperFees: {
  startingFee: 666_777,   // 66.7777% — punishes bots in the first seconds
  endingFee: 41_673,      // 4.1673% — normal fee after decay
  secondsToDecay: 15,     // Linear decay over 15 seconds
},
```

This is built into the Uniswap V4 hook. No additional contracts needed.

---

## Check Unclaimed Rewards

```typescript
const available = await clanker.availableRewards({
  [REDACTED_SECRET],
  rewardRecipient: YOUR_WALLET_ADDRESS,
});

// Parse the response — shape varies by SDK version
// Look for pairedAmount, amount, or total fields
console.log("Unclaimed rewards:", available);
```

---

## Claim Rewards

### Direct Claim (Your Wallet Signs)

```typescript
const { txHash, error } = await clanker.claimRewards({
  [REDACTED_SECRET],
  rewardRecipient: YOUR_WALLET_ADDRESS,
});

if (error) {
  console.error("Claim failed:", error.message);
} else {
  console.log("Claimed:", txHash);
}
```

### Unsigned Transaction (For External Agent Wallets)

If your agent wallet signs externally, get the transaction data without executing:

```typescript
const txConfig = await clanker.getClaimRewardsTransaction({
  [REDACTED_SECRET],
  rewardRecipient: AGENT_WALLET_ADDRESS,
});

if (txConfig && txConfig.address) {
  const data = encodeFunctionData({
    abi: txConfig.abi,
    functionName: txConfig.functionName,
    args: txConfig.args,
  });

  // Return this to the agent wallet to sign
  const unsignedTx = {
    to: txConfig.address,
    data: data,
    value: (txConfig.value || 0).toString(),
    gas: "300000",
  };
}
```

---

## Update Reward Recipient

Change who receives the creator fee share. Only the `admin` of a reward entry can update its recipient.

```typescript
// Step 1: Update the recipient
const { txHash, error } = await clanker.updateRewardRecipient({
  [REDACTED_SECRET],
  rewardIndex: 0n, // Index of the reward entry to update (0 = first recipient)
  newRecipient: NEW_WALLET_ADDRESS,
});

// Step 2: Transfer admin rights to the new recipient
// (so they can manage their own fees going forward)
const { txHash: adminTx, error: adminError } = await clanker.updateRewardAdmin({
  [REDACTED_SECRET],
  rewardIndex: 0n,
  newAdmin: NEW_WALLET_ADDRESS,
});
```

### Unsigned Transaction Version

```typescript
const txConfig = await clanker.getUpdateRewardRecipientTransaction({
  [REDACTED_SECRET],
  rewardIndex: 0n,
  newRecipient: NEW_WALLET_ADDRESS,
});

// Encode and return as unsigned TX (same pattern as claim)
```

### Simulation

Before executing, you can simulate to check validity:

```typescript
await clanker.updateRewardRecipientSimulate({
  [REDACTED_SECRET],
  rewardIndex: 0n,
  newRecipient: NEW_WALLET_ADDRESS,
});
```

---

---

## Key Constants

```typescript
// Reward BPS
const FULL_REWARD = 10_000;    // 100% of pool fees to one recipient

// Sniper protection defaults
const DEFAULT_SNIPER_FEES = {
  startingFee: 666_777,   // ~66.7%
  endingFee: 41_673,      // ~4.2%
  secondsToDecay: 15,
};

// Token supply is always 1 billion (set by Clanker)
// Liquidity is locked (set by Clanker)
```

---

## Error Handling

The Clanker SDK returns `{ txHash, waitForTransaction, error }` from deploy and `{ txHash, error }` from other operations. Always check `error` before proceeding:

```typescript
const { txHash, error } = await clanker.deploy({ ... });

if (error) {
  // Handle: insufficient gas, invalid params, chain not supported, etc.
  console.error(error.message);
  return;
}

// Wait for on-chain confirmation
const { address } = await waitForTransaction();
```

Common errors:
- Insufficient gas in the signing wallet
- Invalid chain ID
- Reward BPS don't total 10,000
- Stable pairing not available on the target chain
- RPC connection issues


path: references/flaunch.md
content: # Flaunch — Direct Mode Reference

> **This file is developer documentation only.** The code examples below are reference patterns for developers to implement in their own applications. This skill does not execute code or access private keys at runtime.

Launch tokens on Base with bonding curves, fair launch periods, and custom fee split managers. Fine-grained control over fee distribution through deployed manager contracts.

**Publisher:** Quick Intel / Web3 Collective — https://quickintel.io
**Source:** https://github.com/Quick-Intel/openclaw-skills/tree/main/token-launcher
**Flaunch docs:** https://docs.flaunch.gg/for-builders

---

## Setup

```bash
npm install viem
```

```typescript
import {
  createPublicClient,
  createWalletClient,
  http,
  encodeFunctionData,
  encodeAbiParameters,
  formatEther,
} from "viem";
import { base } from "viem/chains";

// Initialize viem clients for Base
// See viem docs for wallet/account setup: https://viem.sh/docs/clients/wallet
const publicClient = createPublicClient({ chain: base, transport: http("YOUR_RPC_URL") });
const walletClient = createWalletClient({ account: yourAccount, chain: base, transport: http("YOUR_RPC_URL") });
```

> **Wallet setup:** Use viem's account abstractions to load your signing account securely. See the [viem documentation](https://viem.sh/docs/accounts/local) for options. Use a dedicated launch wallet — see the [Security section in REFERENCE.md](../REFERENCE.md).

You'll also need the Flaunch contract ABIs:
- `FlaunchZap` — the main launch contract
- `TreasuryManagerFactory` — deploys custom fee managers
- `AddressFeeSplitManager` — the fee distribution contract

These are available from the Flaunch docs or by reading the verified contracts on Basescan.

### Code Pattern Note

The code examples below use `walletClient.writeContract()` for direct execution. To generate unsigned calldata instead (for passing to a separate wallet skill or signer), replace `walletClient.writeContract({ address, abi, functionName, args })` with `encodeFunctionData({ abi, functionName, args })` and return it as an unsigned transaction object with the contract `address` as the `to` field.

---

## Architecture

Flaunch uses a modular architecture for fee management:

```
Token Launch (FlaunchZap.flaunch())
├── Creates ERC-20 token
├── Sets up bonding curve / Uniswap pool
└── Attaches a Treasury Manager
    └── AddressFeeSplitManager (custom, deployed by you)
        ├── Recipient 1: your_wallet (100%)
        └── (or multiple recipients with custom splits)
```

The key insight: the fee manager is a **separate deployed contract** that you create before launching the token. This gives you full control over fee distribution without the token contract itself needing custom logic.

---

## Contract Addresses (Base Mainnet)

These are configured as environment variables in your runtime:

```
FLAUNCH_ZAP_CONTRACT_ADDRESS_BASE=<address>
TREASURY_MANAGER_FACTORY_ADDRESS_BASE=<address>
ADDRESS_FEE_SPLIT_MANAGER_IMPLEMENTATION_BASE=<address>
```

Check the Flaunch docs for the latest deployed addresses.

---

## Launch a Token

### Step 1: Create a Custom Fee Split Manager

```typescript
// Define your fee split — 100% to yourself
const recipientShares = [
  {
    recipient: YOUR_WALLET_ADDRESS,
    share: BigInt(100_00000), // 100% in Flaunch basis points (share * 100000)
  },
];

// Encode initialization params
const initializeParams = encodeAbiParameters(
  [
    {
      type: "tuple",
      components: [
        { name: "creatorShare", type: "uint256" },
        {
          name: "recipientShares",
          type: "tuple[]",
          components: [
            { name: "recipient", type: "address" },
            { name: "share", type: "uint256" },
          ],
        },
      ],
    },
  ],
  [
    {
      creatorShare: BigInt(0), // All goes to recipients
      recipientShares: recipientShares,
    },
  ]
);

// Deploy the manager
const managerTxHash = await walletClient.writeContract({
  address: TREASURY_MANAGER_FACTORY_ADDRESS,
  abi: treasuryManagerFactoryAbi,
  functionName: "deployAndInitializeManager",
  args: [
    FEE_SPLIT_MANAGER_IMPLEMENTATION, // Implementation address
    OWNER_ADDRESS,                     // Manager owner
    initializeParams,
  ],
});

const receipt = await publicClient.waitForTransactionReceipt({
  hash: managerTxHash,
});

// Extract the deployed manager address from the receipt logs
const customManagerAddress = getDeployedManagerAddress(receipt.logs);
```

### Step 2: Launch the Token

```typescript
const flaunchParams = {
  name: "Galaxy Cat",
  symbol: "GCAT",
  tokenUri: IPFS_METADATA_URI,          // Upload metadata to IPFS first
  initialTokenFairLaunch: BigInt("60000000000000000000000000000"), // 60% of supply
  fairLaunchDuration: BigInt(30 * 60),   // 30 minute fair launch
  premineAmount: BigInt(0),
  creator: YOUR_WALLET_ADDRESS,
  creatorFeeAllocation: 100 * 100,       // 100% managed by the fee split manager
  flaunchAt: BigInt(0),                  // Launch immediately
  initialPriceParams: DEFAULT_INITIAL_PRICE_PARAMS,
  feeCalculatorParams: "0x",
};

const whitelistParams = {
  merkleRoot: "0x" + "0".repeat(64),
  merkleIPFSHash: "",
  maxTokens: BigInt(0),
};

const airdropParams = {
  airdropIndex: BigInt(0),
  airdropAmount: BigInt(0),
  airdropEndTime: BigInt(0),
  merkleRoot: "0x" + "0".repeat(64),
  merkleIPFSHash: "",
};

const treasuryManagerParams = {
  manager: customManagerAddress,   // Your deployed fee split manager
  initializeData: "0x",
  depositData: "0x",
};

const hash = await walletClient.writeContract({
  address: FLAUNCH_ZAP_ADDRESS,
  abi: flaunchZapAbi,
  functionName: "flaunch",
  args: [flaunchParams, whitelistParams, airdropParams, treasuryManagerParams],
});

const receipt = await publicClient.waitForTransactionReceipt({ hash });
const tokenAddress = getMemecoinAddress(receipt.logs, "standard");
```

### Multiple Recipients (e.g., 70/30 Split)

```typescript
const recipientShares = [
  {
    recipient: CREATOR_WALLET,
    share: BigInt(70_00000), // 70%
  },
  {
    recipient: TREASURY_WALLET,
    share: BigInt(30_00000), // 30%
  },
];
```

Shares must total `100_00000` (100%).

---

## Check Unclaimed Fees

Call `balances(recipientAddress)` on the fee split manager contract:

```typescript
const feeManagerAbi = [
  {
    inputs: [{ name: "_recipient", type: "address" }],
    name: "balances",
    outputs: [{ name: "balance_", type: "uint256" }],
    stateMutability: "view",
    type: "function",
  },
];

const balance = await publicClient.readContract({
  address: FEE_MANAGER_ADDRESS,
  abi: feeManagerAbi,
  functionName: "balances",
  args: [YOUR_WALLET_ADDRESS],
});

const balanceEth = parseFloat(formatEther(balance));
console.log(`Unclaimed fees: ${balanceEth} ETH`);
```

---

## Claim Fees

Call `claim()` on the fee split manager. The caller receives their share:

```typescript
const claimAbi = [
  {
    inputs: [],
    name: "claim",
    outputs: [{ name: "", type: "uint256" }],
    stateMutability: "nonpayable",
    type: "function",
  },
];

// Direct execution
const hash = await walletClient.writeContract({
  address: FEE_MANAGER_ADDRESS,
  abi: claimAbi,
  functionName: "claim",
});

// Or build unsigned transaction for external wallets
const data = encodeFunctionData({
  abi: claimAbi,
  functionName: "claim",
});

const unsignedTx = {
  to: FEE_MANAGER_ADDRESS,
  data: data,
  value: "0",
  gas: "200000",
};
```

---

## Transfer Fee Recipient (Update Who Gets Paid)

Call `transferRecipientShare(newRecipient)` on the fee manager. Must be called by the current recipient.

**Important:** Claim any accumulated fees before transferring — unclaimed fees belong to the current recipient and may not be claimable after transfer.

```typescript
const transferAbi = [
  {
    inputs: [{ name: "_newRecipient", type: "address" }],
    name: "transferRecipientShare",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
];

// Step 1: Claim existing fees first
await walletClient.writeContract({
  address: FEE_MANAGER_ADDRESS,
  abi: claimAbi,
  functionName: "claim",
});

// Step 2: Transfer the share to the new recipient
await walletClient.writeContract({
  address: FEE_MANAGER_ADDRESS,
  abi: transferAbi,
  functionName: "transferRecipientShare",
  args: [NEW_RECIPIENT_ADDRESS],
});
```

### Unsigned Transaction Version

```typescript
const data = encodeFunctionData({
  abi: transferAbi,
  functionName: "transferRecipientShare",
  args: [NEW_RECIPIENT_ADDRESS],
});

const unsignedTx = {
  to: FEE_MANAGER_ADDRESS,
  data: data,
  value: "0",
  gas: "200000",
};
```

---

## Gas Estimation

Flaunch operations can vary in gas cost. Always estimate with a buffer:

```typescript
let gas;
try {
  const estimate = await publicClient.estimateContractGas({
    address: CONTRACT_ADDRESS,
    abi: contractAbi,
    functionName: "functionName",
    args: [...],
    account: YOUR_ADDRESS,
  });
  gas = (estimate * 150n) / 100n; // 50% buffer
} catch (e) {
  gas = BigInt(200000); // Fallback default
}
```

---

## Key Constants

```typescript
// Default token supply: 100 billion (60% in fair launch)
const DEFAULT_INITIAL_SUPPLY = BigInt("60000000000000000000000000000");

// Default fair launch: 30 minutes
const DEFAULT_FAIR_LAUNCH_DURATION = BigInt(30 * 60);

// Fee split basis points: share * 100000
// 100% = 100_00000
// 90%  = 90_00000
// 10%  = 10_00000
```

---

## Important Notes

- Flaunch is currently **Base only**
- The fee split manager is a separate deployed contract — you need to track its address alongside the token address
- Only the **current recipient** can call `transferRecipientShare` — unlike Clanker where the admin can update
- `claim()` can be called by anyone but only pays out to the registered recipients
- Fair launch duration prevents sniping by design — all purchases during the fair launch period get the same price


path: references/pumpfun.md
content: # Pump.fun — Direct Mode Reference

> **This file is developer documentation only.** The code examples below are reference patterns for developers to implement in their own applications. This skill does not execute code or access private keys at runtime.

Launch SPL tokens on Solana with bonding curves that graduate to Raydium. Manual instruction building required due to Anchor IDL constraints. Supports custom fee sharing with configurable shareholder splits.

**Publisher:** Quick Intel / Web3 Collective — https://quickintel.io
**Source:** https://github.com/Quick-Intel/openclaw-skills/tree/main/token-launcher
**Pump.fun docs:** https://github.com/pump-fun/pump-public-docs

---

## Setup

```bash
npm install @solana/web3.js @solana/spl-token
```

```typescript
import {
  Connection,
  PublicKey,
  Transaction,
  TransactionInstruction,
  SystemProgram,
  Keypair,
  LAMPORTS_PER_SOL,
  ComputeBudgetProgram,
} from "@solana/web3.js";
import {
  TOKEN_2022_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID,
  getAssociatedTokenAddressSync,
} from "@solana/spl-token";

// Initialize connection and load your bot wallet keypair
// See Solana docs for secure key management: https://solana.com/docs/clients/javascript
const connection = new Connection("YOUR_SOLANA_RPC_URL", "confirmed");
const botWallet = loadYourKeypairSecurely(); // Load from secrets manager — see Security section
```

> **Security: Bot Wallet Model.** The code patterns below show how Pump.fun's Solana integration works. Solana requires a signing keypair (unlike EVM where you can generate unsigned calldata). Use a dedicated wallet funded with only ~0.05 SOL for gas, never your main wallet. See the [Security section in REFERENCE.md](../REFERENCE.md) and the [Solana key management docs](https://solana.com/docs/clients/javascript) for secure loading patterns.

---

## Why Manual Instruction Building

Pump.fun uses Anchor, but the IDL has compatibility issues with many client environments. The reliable approach is building instructions manually with the correct discriminators and account layouts. This is what production implementations use.

---

## Program IDs

```typescript
const PUMP_PROGRAM_ID = new PublicKey([REDACTED_SECRET]);
const PUMP_FEES_PROGRAM_ID = new PublicKey([REDACTED_SECRET]);
const PUMP_AMM_PROGRAM_ID = new PublicKey([REDACTED_SECRET]);
const GLOBAL_PDA = new PublicKey([REDACTED_SECRET]);
const MINT_AUTHORITY_PDA = new PublicKey([REDACTED_SECRET]);
const EVENT_AUTHORITY_PDA = new PublicKey([REDACTED_SECRET]);
const MAYHEM_PROGRAM_ID = new PublicKey([REDACTED_SECRET]);
const WSOL_MINT = new PublicKey([REDACTED_SECRET]);
const SPL_TOKEN_PROGRAM_ID = new PublicKey([REDACTED_SECRET]);
```

---

## Instruction Discriminators

These are the first 8 bytes of each instruction (Anchor convention — SHA256 of `global:<function_name>`):

```typescript
const CREATE_V2 = Buffer.from([214, 144, 76, 236, 95, 139, 49, 180]);
const CREATE_FEE_SHARING_CONFIG = Buffer.from([195, 78, 86, 76, 111, 52, 251, 213]);
const UPDATE_FEE_SHARES = Buffer.from([189, 13, 136, 99, 187, 164, 237, 35]);
const DISTRIBUTE_CREATOR_FEES = Buffer.from([165, 114, 103, 0, 121, 206, 247, 81]);
const TRANSFER_CREATOR_FEES_TO_PUMP = Buffer.from([0x8b, 0x34, 0x86, 0x55, 0xe4, 0xe5, 0x6c, 0xf1]);
```

---

## Borsh Serialization Helper

Solana instructions use Borsh serialization for strings:

```typescript
function serializeString(str: string): Buffer {
  const strBuffer = Buffer.from(str, "utf8");
  const lengthBuffer = Buffer.alloc(4);
  lengthBuffer.writeUInt32LE(strBuffer.length, 0);
  return Buffer.concat([lengthBuffer, strBuffer]);
}
```

---

## Launch a Token

### Step 1: Create the Token (createV2)

```typescript
const mintKeypair = Keypair.generate();

// Derive PDAs
const [bondingCurve] = PublicKey.findProgramAddressSync(
  [Buffer.from("bonding-curve"), mintKeypair.publicKey.toBuffer()],
  PUMP_PROGRAM_ID
);

const [associatedBondingCurve] = PublicKey.findProgramAddressSync(
  [
    bondingCurve.toBuffer(),
    TOKEN_2022_PROGRAM_ID.toBuffer(),
    mintKeypair.publicKey.toBuffer(),
  ],
  ASSOCIATED_TOKEN_PROGRAM_ID
);

const [creatorVault] = PublicKey.findProgramAddressSync(
  [Buffer.from("creator-vault"), botWallet.publicKey.toBuffer()],
  PUMP_PROGRAM_ID
);

const [globalParams] = PublicKey.findProgramAddressSync(
  [Buffer.from("global-params")],
  MAYHEM_PROGRAM_ID
);

const [solVault] = PublicKey.findProgramAddressSync(
  [Buffer.from("sol-vault")],
  MAYHEM_PROGRAM_ID
);

const [mayhemState] = PublicKey.findProgramAddressSync(
  [Buffer.from("mayhem-state"), mintKeypair.publicKey.toBuffer()],
  MAYHEM_PROGRAM_ID
);

// Build instruction data
const instructionData = Buffer.concat([
  CREATE_V2,
  serializeString("Galaxy Cat"),           // name
  serializeString("GCAT"),                 // symbol
  serializeString(METADATA_IPFS_URI),      // metadata URI
  botWallet.publicKey.toBuffer(),          // creator (bot wallet is on-chain creator)
  Buffer.from([0]),                        // is_mayhem_mode: false
]);

// Account metas (must match IDL order exactly)
const keys = [
  { pubkey: mintKeypair.publicKey, isSigner: true, isWritable: true },
  { pubkey: MINT_AUTHORITY_PDA, isSigner: false, isWritable: false },
  { pubkey: bondingCurve, isSigner: false, isWritable: true },
  { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
  { pubkey: GLOBAL_PDA, isSigner: false, isWritable: false },
  { pubkey: botWallet.publicKey, isSigner: true, isWritable: true },
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  { pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: MAYHEM_PROGRAM_ID, isSigner: false, isWritable: true },
  { pubkey: globalParams, isSigner: false, isWritable: false },
  { pubkey: solVault, isSigner: false, isWritable: true },
  { pubkey: mayhemState, isSigner: false, isWritable: true },
  { pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
  { pubkey: EVENT_AUTHORITY_PDA, isSigner: false, isWritable: false },
  { pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
];

const createInstruction = new TransactionInstruction({
  keys,
  programId: PUMP_PROGRAM_ID,
  data: instructionData,
});

// Build and send transaction
const transaction = new Transaction();
transaction.add(
  ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
  ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1_000_000 })
);
transaction.add(createInstruction);

const { blockhash } = await connection.getLatestBlockhash("confirmed");
transaction.recentBlockhash = blockhash;
transaction.feePayer = botWallet.publicKey;
// Sign with both the bot wallet and mint keypair, then send
// See Solana docs: https://solana.com/docs/clients/javascript#transactions

const txHash = await connection.sendRawTransaction(transaction.serialize(), {
  skipPreflight: false,
  maxRetries: 3,
});
await connection.confirmTransaction(txHash, "confirmed");
```

### Step 2: Set Up Fee Sharing

Fee sharing requires TWO instructions in a single transaction:

1. `create_fee_sharing_config` — creates the sharing config PDA
2. `update_fee_shares` — sets the shareholders and their BPS

```typescript
const [sharingConfig] = PublicKey.findProgramAddressSync(
  [Buffer.from("sharing-config"), mintKeypair.publicKey.toBuffer()],
  PUMP_FEES_PROGRAM_ID
);

const [feesEventAuthority] = PublicKey.findProgramAddressSync(
  [Buffer.from("__event_authority")],
  PUMP_FEES_PROGRAM_ID
);

const [pumpEventAuthority] = PublicKey.findProgramAddressSync(
  [Buffer.from("__event_authority")],
  PUMP_PROGRAM_ID
);

const [ammEventAuthority] = PublicKey.findProgramAddressSync(
  [Buffer.from("__event_authority")],
  PUMP_AMM_PROGRAM_ID
);

// === Instruction 1: create_fee_sharing_config ===

const createConfigKeys = [
  { pubkey: feesEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: botWallet.publicKey, isSigner: true, isWritable: true },
  { pubkey: GLOBAL_PDA, isSigner: false, isWritable: false },
  { pubkey: mintKeypair.publicKey, isSigner: false, isWritable: false },
  { pubkey: sharingConfig, isSigner: false, isWritable: true },
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  { pubkey: bondingCurve, isSigner: false, isWritable: true },
  { pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: pumpEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: ammEventAuthority, isSigner: false, isWritable: false },
];

const createConfigIx = new TransactionInstruction({
  keys: createConfigKeys,
  programId: PUMP_FEES_PROGRAM_ID,
  data: CREATE_FEE_SHARING_CONFIG,
});

// === Instruction 2: update_fee_shares ===

const [pumpCreatorVault] = PublicKey.findProgramAddressSync(
  [Buffer.from("creator-vault"), sharingConfig.toBuffer()],
  PUMP_PROGRAM_ID
);

const [coinCreatorVaultAuthority] = PublicKey.findProgramAddressSync(
  [Buffer.from("creator_vault"), sharingConfig.toBuffer()],
  PUMP_AMM_PROGRAM_ID
);

const coinCreatorVaultAta = getAssociatedTokenAddressSync(
  WSOL_MINT,
  coinCreatorVaultAuthority,
  true,
  SPL_TOKEN_PROGRAM_ID
);

// Serialize shareholders: 100% to your wallet
const shareholdersCount = Buffer.alloc(4);
shareholdersCount.writeUInt32LE(1, 0); // 1 shareholder

const feeBps = Buffer.alloc(2);
feeBps.writeUInt16LE(10000, 0); // 100% = 10000 BPS

const shareholder = Buffer.concat([
  YOUR_SOLANA_WALLET.toBuffer(), // Your wallet gets 100%
  feeBps,
]);

const updateFeesData = Buffer.concat([
  UPDATE_FEE_SHARES,
  shareholdersCount,
  shareholder,
]);

const updateFeesKeys = [
  { pubkey: feesEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: botWallet.publicKey, isSigner: true, isWritable: true },
  { pubkey: GLOBAL_PDA, isSigner: false, isWritable: false },
  { pubkey: mintKeypair.publicKey, isSigner: false, isWritable: false },
  { pubkey: sharingConfig, isSigner: false, isWritable: true },
  { pubkey: bondingCurve, isSigner: false, isWritable: false },
  { pubkey: pumpCreatorVault, isSigner: false, isWritable: true },
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  { pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: pumpEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: ammEventAuthority, isSigner: false, isWritable: false },
  { pubkey: WSOL_MINT, isSigner: false, isWritable: false },
  { pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: coinCreatorVaultAuthority, isSigner: false, isWritable: true },
  { pubkey: coinCreatorVaultAta, isSigner: false, isWritable: true },
  { pubkey: botWallet.publicKey, isSigner: false, isWritable: false },
];

const updateFeesIx = new TransactionInstruction({
  keys: updateFeesKeys,
  programId: PUMP_FEES_PROGRAM_ID,
  data: updateFeesData,
});

// Both instructions in one transaction
const feeTx = new Transaction();
feeTx.add(
  ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
  ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1_000_000 })
);
feeTx.add(createConfigIx);
feeTx.add(updateFeesIx);
// Sign and send...
```

### Multiple Shareholders

```typescript
// 70% to creator, 30% to treasury
const shareholdersCount = Buffer.alloc(4);
shareholdersCount.writeUInt32LE(2, 0);

const bps1 = Buffer.alloc(2);
bps1.writeUInt16LE(7000, 0); // 70%

const bps2 = Buffer.alloc(2);
bps2.writeUInt16LE(3000, 0); // 30%

const shareholder1 = Buffer.concat([CREATOR_PUBKEY.toBuffer(), bps1]);
const shareholder2 = Buffer.concat([TREASURY_PUBKEY.toBuffer(), bps2]);

const data = Buffer.concat([
  UPDATE_FEE_SHARES,
  shareholdersCount,
  shareholder1,
  shareholder2,
]);
```

Total BPS must equal 10,000.

---

## Graduation: Pre vs Post

Pump.fun tokens start on a bonding curve. Once the market cap threshold is hit, the token "graduates" to Raydium AMM. This changes how fees accumulate:

```
PRE-GRADUATION:
  Swap fees → Creator Vault (native SOL)
  └── distribute_creator_fees → shareholders

POST-GRADUATION:
  Swap fees → AMM Coin Creator Vault (WSOL)
  └── transfer_creator_fees_to_pump → unwrap WSOL → Creator Vault (SOL)
      └── distribute_creator_fees → shareholders
```

### Detecting Graduation

Read the bonding curve account and check the `complete` field at byte offset 48:

```typescript
const [bondingCurve] = PublicKey.findProgramAddressSync(
  [Buffer.from("bonding-curve"), tokenMint.toBuffer()],
  PUMP_PROGRAM_ID
);

const accountInfo = await connection.getAccountInfo(bondingCurve);
// Layout: 8 byte discriminator + 5x u64 (40 bytes) = offset 48
const isGraduated = accountInfo.data[48] === 1;
```

---

## Check Unclaimed Fees

Check both vaults — pump vault (native SOL) and AMM vault (WSOL):

```typescript
const [sharingConfig] = PublicKey.findProgramAddressSync(
  [Buffer.from("sharing-config"), tokenMint.toBuffer()],
  PUMP_FEES_PROGRAM_ID
);

const [pumpCreatorVault] = PublicKey.findProgramAddressSync(
  [Buffer.from("creator-vault"), sharingConfig.toBuffer()],
  PUMP_PROGRAM_ID
);

// Always check pump vault (native SOL)
const pumpBalance = await connection.getBalance(pumpCreatorVault);
const pumpSOL = pumpBalance / LAMPORTS_PER_SOL;

// If graduated, also check AMM vault (WSOL)
let ammWSOL = 0;
if (isGraduated) {
  const [vaultAuthority] = PublicKey.findProgramAddressSync(
    [Buffer.from("creator_vault"), sharingConfig.toBuffer()],
    PUMP_AMM_PROGRAM_ID
  );

  const vaultAta = getAssociatedTokenAddressSync(
    WSOL_MINT,
    vaultAuthority,
    true,
    SPL_TOKEN_PROGRAM_ID
  );

  try {
    const tokenBalance = await connection.getTokenAccountBalance(vaultAta);
    ammWSOL = parseFloat(tokenBalance.value.uiAmountString || "0");
  } catch {
    ammWSOL = 0; // ATA may not exist yet
  }
}

const totalUnclaimed = pumpSOL + ammWSOL;
```

---

## Claim Fees

### Pre-Graduation (SOL only)

One instruction — `distribute_creator_fees`:

```typescript
const distributeKeys = [
  { pubkey: tokenMint, isSigner: false, isWritable: false },
  { pubkey: bondingCurve, isSigner: false, isWritable: false },
  { pubkey: sharingConfig, isSigner: false, isWritable: false },
  { pubkey: pumpCreatorVault, isSigner: false, isWritable: true },
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  { pubkey: pumpEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_PROGRAM_ID, isSigner: false, isWritable: false },
  // Append ALL shareholders as remaining accounts (writable, signer if it's the bot)
  ...shareholders.map((s) => ({
    pubkey: s.address,
    isSigner: s.address.equals(botWallet.publicKey),
    isWritable: true,
  })),
];

const distributeIx = new TransactionInstruction({
  keys: distributeKeys,
  programId: PUMP_PROGRAM_ID,
  data: DISTRIBUTE_CREATOR_FEES,
});
```

### Post-Graduation (WSOL → SOL → Shareholders)

Two instructions in one transaction:

```typescript
const transaction = new Transaction();
transaction.add(
  ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
  ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1_000_000 })
);

// Instruction 1: Transfer WSOL from AMM vault → unwrap → pump vault
const transferKeys = [
  { pubkey: WSOL_MINT, isSigner: false, isWritable: false },
  { pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
  { pubkey: sharingConfig, isSigner: false, isWritable: false },         // coin_creator
  { pubkey: ammCoinCreatorVaultAuthority, isSigner: false, isWritable: true },
  { pubkey: ammCoinCreatorVaultAta, isSigner: false, isWritable: true },
  { pubkey: pumpCreatorVault, isSigner: false, isWritable: true },
  { pubkey: ammEventAuthority, isSigner: false, isWritable: false },
  { pubkey: PUMP_AMM_PROGRAM_ID, isSigner: false, isWritable: false },
];

transaction.add(
  new TransactionInstruction({
    keys: transferKeys,
    programId: PUMP_AMM_PROGRAM_ID,
    data: TRANSFER_CREATOR_FEES_TO_PUMP,
  })
);

// Instruction 2: Distribute SOL from pump vault to shareholders
transaction.add(distributeIx); // Same as pre-graduation

// Sign and send
```

### Minimum Claimable Amount

Pump.fun requires a minimum balance in the creator vault before distribution will succeed:

```typescript
const MIN_DISTRIBUTABLE_LAMPORTS = 9_799_680; // ~0.0098 SOL
```

Check the vault balance exceeds this before attempting to claim.

---

## Update Fee Recipient

Use `update_fee_shares` with the new shareholders in the instruction data and the **current** shareholders as remaining accounts:

```typescript
// New shareholders (what you're updating TO)
const newShareholderData = Buffer.concat([
  UPDATE_FEE_SHARES,
  shareholdersCount,
  Buffer.concat([NEW_RECIPIENT.toBuffer(), newBps]),  // New shareholder
]);

// Account keys — current shareholders go as remaining accounts
const updateKeys = [
  // ... fixed accounts (same as initial setup) ...
  // REMAINING ACCOUNTS: Current shareholders for validation
  { pubkey: CURRENT_RECIPIENT, isSigner: false, isWritable: true },
];
```

**The key insight:** The instruction data contains the NEW shareholders, but the remaining accounts must contain the CURRENT shareholders. The program validates that the current state matches before allowing the update.

---

## Parsing Shareholders from Account Data

Read the sharing config PDA to see current shareholders:

```typescript
// SharingConfig layout:
//   8  bytes - Anchor discriminator
//   1  byte  - bump
//   1  byte  - version
//   1  byte  - status (0=Paused, 1=Active)
//   32 bytes - mint pubkey
//   32 bytes - admin pubkey
//   1  byte  - admin_revoked
//   4  bytes - shareholders vec length (u32 LE)
//   N × 34  - each Shareholder { address: 32 bytes, share_bps: u16 }

const VEC_OFFSET = 76; // 8 + 1 + 1 + 1 + 32 + 32 + 1

function parseShareholders(data: Buffer) {
  const count = data.readUInt32LE(VEC_OFFSET);
  const shareholders = [];
  let offset = VEC_OFFSET + 4;

  for (let i = 0; i < count; i++) {
    const address = new PublicKey(data.slice(offset, offset + 32));
    offset += 32;
    const shareBps = data.readUInt16LE(offset);
    offset += 2;
    shareholders.push({ address, shareBps });
  }

  return shareholders;
}
```

---

## Bot Wallet Model

Unlike Clanker and Flaunch where the user's wallet can sign transactions directly, Pump.fun requires a **bot wallet** pattern:

- The bot wallet is the on-chain "creator" (it signs the `createV2` instruction)
- The bot wallet signs fee sharing and claim transactions
- The actual user/agent is set as a shareholder to receive fees
- The bot wallet needs SOL for gas (minimum ~0.02 SOL per launch)

**This means:** For Pump.fun, your agent needs a dedicated Solana keypair that stays funded. This is different from the EVM model where you can return unsigned transactions for any external wallet to sign.

---

## Metadata Upload

Pump.fun expects metadata at an IPFS URI. Upload a JSON file:

```json
{
  "name": "Galaxy Cat",
  "symbol": "GCAT",
  "description": "Galaxy Cat ($GCAT) - The first feline in the cosmos",
  "image": "https://example.com/gcat.png",
  "twitter": ""
}
```

You can use pump.fun's own IPFS endpoint or any IPFS pinning service.

---

## Important Notes

- **Solana only** — Pump.fun doesn't support EVM chains
- **Token-2022 standard** — not legacy SPL tokens
- **Bot wallet required** — Solana's signing model means you can't return unsigned transactions for an external wallet to sign (all signers must be present)
- **Graduation is one-way** — once graduated to Raydium, the bonding curve is permanently closed
- **Minimum claim threshold** — ~0.0098 SOL must be in the vault before distribution works
- **Fee sharing config is per-token** — each token has its own sharing config PDA derived from the mint
- **Admin can be revoked** — once admin is revoked on the sharing config, no more shareholder updates are possible


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)