skills/tokens/token-2022-extensions/SKILL.md
Implement Token-2022 (Token Extensions) features - transfer fees, permanent delegate, metadata pointer, confidential transfers, and more. Use when creating advanced tokens or analyzing Token-2022 tokens.
npx skillsauth add sanctifiedops/solana-skills token-2022-extensionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Role framing: You are a Solana token engineer specializing in Token-2022 (Token Extensions). Your goal is to help developers implement advanced token features and understand the implications of each extension.
| Extension | Use Case | Authority Needed | |-----------|----------|------------------| | Transfer Fee | Take % of each transfer | Fee authority | | Permanent Delegate | Reclaim/burn tokens anytime | Delegate (permanent) | | Non-Transferable | Soulbound tokens | None (built-in) | | Metadata Pointer | On-chain metadata | Update authority | | Interest-Bearing | Display accrued interest | Rate authority | | Default Account State | Frozen by default | Freeze authority | | Transfer Hook | Custom logic on transfer | Hook authority | | Confidential Transfers | Hide amounts | None (user-controlled) | | CPI Guard | Protect from malicious CPI | None (user opt-in) | | Memo Required | Force memo on transfers | None (built-in) |
import {
createInitializeMintInstruction,
createInitializeTransferFeeConfigInstruction,
createInitializeMetadataPointerInstruction,
ExtensionType,
getMintLen,
TOKEN_2022_PROGRAM_ID,
} from '@solana/spl-token';
import {
Connection,
Keypair,
SystemProgram,
Transaction,
} from '@solana/web3.js';
async function createToken2022WithExtensions(
connection: Connection,
payer: Keypair,
mintAuthority: PublicKey,
extensions: ExtensionType[]
): Promise<PublicKey> {
const mintKeypair = Keypair.generate();
// Calculate required space
const mintLen = getMintLen(extensions);
// Get rent
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
const transaction = new Transaction();
// Create account
transaction.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: mintLen,
lamports,
programId: TOKEN_2022_PROGRAM_ID,
})
);
// Initialize extensions (order matters!)
// Extensions must be initialized BEFORE InitializeMint
// ... add extension init instructions here ...
// Initialize mint (must be last)
transaction.add(
createInitializeMintInstruction(
mintKeypair.publicKey,
6, // decimals
mintAuthority,
null, // freeze authority
TOKEN_2022_PROGRAM_ID
)
);
await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]);
return mintKeypair.publicKey;
}
Most common extension for revenue-generating tokens:
import {
createInitializeTransferFeeConfigInstruction,
createSetTransferFeeInstruction,
harvestWithheldTokensToMint,
withdrawWithheldTokensFromMint,
} from '@solana/spl-token';
// Initialize transfer fee (during mint creation)
const transferFeeConfig = createInitializeTransferFeeConfigInstruction(
mintKeypair.publicKey,
transferFeeConfigAuthority, // Can update fee
withdrawWithheldAuthority, // Can withdraw collected fees
feeBasisPoints, // e.g., 100 = 1%
maxFee, // Maximum fee in token base units
TOKEN_2022_PROGRAM_ID
);
// Fee collection flow:
// 1. Fees accumulate in recipient token accounts (withheld)
// 2. Harvest: Move withheld fees to mint
// 3. Withdraw: Move from mint to treasury
// Harvest fees from all accounts
await harvestWithheldTokensToMint(
connection,
payer,
mint,
tokenAccounts, // Accounts with withheld fees
);
// Withdraw to treasury
await withdrawWithheldTokensFromMint(
connection,
payer,
mint,
treasuryAccount,
withdrawAuthority,
);
Fee calculation:
fee = min(transferAmount * feeBasisPoints / 10000, maxFee)
Example: 1% fee with 1M max fee
- Transfer 1,000 tokens → Fee: 10 tokens
- Transfer 1B tokens → Fee: 1M tokens (capped)
Allows authority to transfer/burn from any holder:
import { createInitializePermanentDelegateInstruction } from '@solana/spl-token';
// Initialize during mint creation
const permanentDelegateIx = createInitializePermanentDelegateInstruction(
mintKeypair.publicKey,
delegateAuthority, // WARNING: This address can take tokens from anyone
TOKEN_2022_PROGRAM_ID
);
// Use cases:
// - Reclaiming tokens from lost wallets
// - Compliance/clawback requirements
// - Subscription models (auto-debit)
// WARNING: This is powerful and scary to users
// Clearly disclose this capability
On-chain metadata without Metaplex:
import {
createInitializeMetadataPointerInstruction,
createInitializeInstruction as createInitializeMetadataInstruction,
createUpdateFieldInstruction,
pack,
TokenMetadata,
} from '@solana/spl-token-metadata';
// Point metadata to the mint itself (self-referential)
const metadataPointerIx = createInitializeMetadataPointerInstruction(
mintKeypair.publicKey,
updateAuthority,
mintKeypair.publicKey, // Metadata stored on mint account
TOKEN_2022_PROGRAM_ID
);
// Initialize metadata
const metadata: TokenMetadata = {
mint: mintKeypair.publicKey,
name: 'My Token',
symbol: 'MTK',
uri: 'https://example.com/metadata.json',
additionalMetadata: [
['website', 'https://mytoken.com'],
['twitter', '@mytoken'],
],
};
const initMetadataIx = createInitializeMetadataInstruction({
programId: TOKEN_2022_PROGRAM_ID,
mint: mintKeypair.publicKey,
metadata: mintKeypair.publicKey,
name: metadata.name,
symbol: metadata.symbol,
uri: metadata.uri,
mintAuthority: mintAuthority,
updateAuthority: updateAuthority,
});
import { ExtensionType, createInitializeNonTransferableMintInstruction } from '@solana/spl-token';
// Add to extensions array
const extensions = [ExtensionType.NonTransferable];
// Initialize during mint creation
const nonTransferableIx = createInitializeNonTransferableMintInstruction(
mintKeypair.publicKey,
TOKEN_2022_PROGRAM_ID
);
// These tokens:
// - Cannot be transferred between wallets
// - Can still be burned
// - Can be minted to any wallet
// Use case: Credentials, achievements, membership
Custom logic on every transfer:
import { createInitializeTransferHookInstruction } from '@solana/spl-token';
// Initialize hook (points to your program)
const transferHookIx = createInitializeTransferHookInstruction(
mintKeypair.publicKey,
hookAuthority,
hookProgramId, // Your custom program
TOKEN_2022_PROGRAM_ID
);
// Your hook program must implement:
// - Execute: Called on every transfer
// - Must not fail (or transfer fails)
// Use cases:
// - Allowlist/blocklist enforcement
// - Dynamic fee calculation
// - Transfer logging
// - Royalty enforcement
// Check DEX compatibility before launch
const DEX_SUPPORT = {
jupiter: {
transferFee: true,
permanentDelegate: false, // Users scared
metadataPointer: true,
nonTransferable: false,
transferHook: 'limited', // Some hooks
confidentialTransfers: false,
},
raydium: {
transferFee: true,
// Check current status: https://docs.raydium.io
},
orca: {
// Token-2022 support in progress
},
};
// Always verify current support before launch
| Combination | Valid | Use Case | |-------------|-------|----------| | Transfer Fee + Metadata | Yes | Fee-taking token with on-chain metadata | | Transfer Fee + Permanent Delegate | Yes | Compliant token with fees | | Non-Transferable + Metadata | Yes | Soulbound credentials | | Transfer Fee + Non-Transferable | No | Doesn't make sense | | Transfer Hook + Transfer Fee | Yes | Custom fee logic | | Confidential + Transfer Fee | Partial | Complex interactions |
## Token-2022 Launch Checklist
### Pre-Creation
- [ ] Extensions list finalized
- [ ] Authority addresses determined
- [ ] DEX compatibility verified
- [ ] Wallet support confirmed (Phantom, Solflare)
- [ ] Rent cost calculated
### Creation
- [ ] Extensions initialized in correct order
- [ ] Mint initialized last
- [ ] Authorities set correctly
- [ ] Test mint on devnet first
### Post-Creation
- [ ] Verify extensions on explorer
- [ ] Test transfers with fee collection (if applicable)
- [ ] Verify metadata displays correctly
- [ ] Document authority custody
- [ ] Publish extension disclosure
## $TOKEN Transfer Fee Disclosure
This token uses Token-2022 with Transfer Fee extension.
**Fee Structure:**
- Fee Rate: [X]% ([Y] basis points)
- Maximum Fee: [Z] tokens per transfer
**How Fees Work:**
1. On each transfer, [X]% is withheld from recipient
2. Fees accumulate in token accounts
3. Fees are periodically harvested to treasury
**Authorities:**
- Fee Config Authority: [address] - Can change fee rate
- Withdraw Authority: [address] - Can collect fees
**Fee Usage:**
[Describe how fees will be used]
**DEX Trading:**
Supported on: Jupiter, [other DEXs]
Not supported on: [list any non-supporting DEXs]
Implementation is complete when:
Provide:
Input: "Create a memecoin with 1% transfer fee going to treasury"
Output:
import {
createInitializeTransferFeeConfigInstruction,
createInitializeMintInstruction,
createInitializeMetadataPointerInstruction,
ExtensionType,
getMintLen,
TOKEN_2022_PROGRAM_ID,
} from '@solana/spl-token';
async function createFeeToken(
connection: Connection,
payer: Keypair,
treasury: PublicKey
) {
const mint = Keypair.generate();
// Extensions needed
const extensions = [
ExtensionType.TransferFeeConfig,
ExtensionType.MetadataPointer,
];
const mintLen = getMintLen(extensions);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
const tx = new Transaction();
// 1. Create account
tx.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mint.publicKey,
space: mintLen,
lamports,
programId: TOKEN_2022_PROGRAM_ID,
})
);
// 2. Initialize transfer fee (1% = 100 basis points)
tx.add(
createInitializeTransferFeeConfigInstruction(
mint.publicKey,
payer.publicKey, // Fee authority
treasury, // Withdraw authority
100, // 1% fee
BigInt(1_000_000_000), // Max 1B tokens fee
TOKEN_2022_PROGRAM_ID
)
);
// 3. Initialize metadata pointer
tx.add(
createInitializeMetadataPointerInstruction(
mint.publicKey,
payer.publicKey,
mint.publicKey,
TOKEN_2022_PROGRAM_ID
)
);
// 4. Initialize mint (LAST)
tx.add(
createInitializeMintInstruction(
mint.publicKey,
6,
payer.publicKey,
null,
TOKEN_2022_PROGRAM_ID
)
);
await sendAndConfirmTransaction(connection, tx, [payer, mint]);
console.log('Token created:', mint.publicKey.toBase58());
console.log('Fee: 1% per transfer');
console.log('Treasury can withdraw fees:', treasury.toBase58());
}
Input: "Create a subscription token where we can auto-debit monthly"
Output:
// WARNING: Permanent delegate is powerful
// Users must clearly understand and consent
async function createSubscriptionToken(
connection: Connection,
payer: Keypair,
subscriptionManager: PublicKey // Authority that can debit
) {
const mint = Keypair.generate();
const extensions = [
ExtensionType.PermanentDelegate,
ExtensionType.MetadataPointer,
];
const mintLen = getMintLen(extensions);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
const tx = new Transaction();
tx.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mint.publicKey,
space: mintLen,
lamports,
programId: TOKEN_2022_PROGRAM_ID,
})
);
// Permanent delegate - subscription manager can debit
tx.add(
createInitializePermanentDelegateInstruction(
mint.publicKey,
subscriptionManager, // Can transfer from ANY holder
TOKEN_2022_PROGRAM_ID
)
);
// Metadata for clarity
tx.add(
createInitializeMetadataPointerInstruction(
mint.publicKey,
payer.publicKey,
mint.publicKey,
TOKEN_2022_PROGRAM_ID
)
);
tx.add(
createInitializeMintInstruction(
mint.publicKey,
6,
payer.publicKey,
null,
TOKEN_2022_PROGRAM_ID
)
);
await sendAndConfirmTransaction(connection, tx, [payer, mint]);
// REQUIRED DISCLOSURE:
console.log(`
⚠️ SUBSCRIPTION TOKEN CREATED
This token has PERMANENT DELEGATE enabled.
The subscription manager (${subscriptionManager.toBase58()})
can transfer tokens FROM any holder's wallet.
Use case: Auto-debiting subscription payments
USERS MUST BE CLEARLY INFORMED before receiving this token.
`);
}
development
--- name: transparency-and-disclosures description: Write clear disclosures for Solana projects: risks, unlocks, authority states, and data sources. Use for websites, docs, and announcements. --- # Transparency and Disclosures Role framing: You are a disclosures officer. Your goal is to communicate risks and facts plainly with verifiable links. ## Initial Assessment - What products/tokens are live? What risks exist (smart contract, market, custodial)? - Upcoming events (unlocks, upgrades)? -
testing
Comprehensive rug detection for Solana tokens - red flags, contract analysis, LP verification, insider patterns, and escape routes. Use before buying any token to protect against scams.
development
--- name: reputation-recovery-playbook description: Recover credibility after mistakes: incident comms, restitution, roadmap resets, and monitoring sentiment. Use after exploits, missteps, or comms errors. --- # Reputation Recovery Playbook Role framing: You are a crisis manager. Your goal is to respond to incidents transparently and rebuild trust. ## Initial Assessment - What happened? Impacted users/funds? Root cause known? - Current status (contained/ongoing)? - Evidence available (txids,
development
--- name: legitimacy-signals description: How to project legitimacy for Solana projects: disclosures, address registry, audits, comms patterns, red-flag avoidance. Use for project pages, announcements, and community trust work. --- # Legitimacy Signals Role framing: You are a trust & safety operator for Solana launches. Your goal is to surface credible signals, avoid scams cues, and give buyers clear risk context. ## Initial Assessment - Project type (token, dApp, NFT, bot) and stage (pre-lau