In March 2026, Solana's network degradation isn't coming from a protocol bug — it's coming from an industrial-scale scam token factory exploiting Token-2022's Permanent Delegate extension to burn victims' tokens seconds after purchase. RugCheck.xyz flags over 40% of new Solana tokens as using this extension. Here's how the attack works at the bytecode level, and a complete detection pipeline you can deploy today.
The Permanent Delegate Attack Flow
Token-2022 (SPL Token 2022) introduced token extensions — powerful primitives for compliance, privacy, and programmability. The PermanentDelegate extension grants a designated authority unconditional power to transfer or burn any holder's tokens without their signature.
The intended use case: regulatory compliance (freezing sanctioned addresses). The actual use in 2026: automated theft.
Attack Sequence (Step by Step)
1. Attacker deploys token via Token-2022 with PermanentDelegate = deployer wallet
2. Creates liquidity pool on Raydium/Orca with SOL pairing
3. Generates fake volume via wash trading bots (10-50 coordinated wallets)
4. Victims buy on DEX aggregators — token appears in their wallet
5. Within 1-60 seconds: Permanent Delegate calls Burn on victim's token account
6. Victim's balance → 0, but they already paid SOL
7. Attacker drains LP (sells remaining tokens for SOL)
8. Repeat with new mint address — entire cycle takes < 5 minutes
The On-Chain Signature
Here's what the Permanent Delegate burn instruction looks like in Anchor/Rust:
use anchor_lang::prelude::*;
use anchor_spl::token_2022::{self, Token2022, BurnChecked};
// This is what the ATTACKER's program calls
pub fn burn_victim_tokens(ctx: Context<BurnVictim>, amount: u64, decimals: u8) -> Result<()> {
// No victim signature required — Permanent Delegate has unconditional authority
let cpi_accounts = BurnChecked {
mint: ctx.accounts.mint.to_account_info(),
from: ctx.accounts.victim_token_account.to_account_info(),
authority: ctx.accounts.permanent_delegate.to_account_info(), // attacker's key
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token_2022::burn_checked(cpi_ctx, amount, decimals)?;
Ok(())
}
#[derive(Accounts)]
pub struct BurnVictim<'info> {
pub mint: AccountInfo<'info>,
/// CHECK: Victim's token account — no signature needed
#[account(mut)]
pub victim_token_account: AccountInfo<'info>,
pub permanent_delegate: Signer<'info>, // only the delegate signs
pub token_program: Program<'info, Token2022>,
}
The critical detail: no victim signature appears anywhere. The Permanent Delegate is the sole signer.
Why Existing Tools Miss It
Standard token safety checkers look for:
- ❌ Mint authority (can mint new tokens) — Permanent Delegate doesn't need to mint
- ❌ Freeze authority (can freeze accounts) — Permanent Delegate burns, not freezes
- ❌ LP lock status — attacker can lock LP and still profit via the burn
The Permanent Delegate extension is distinct from mint/freeze authorities. A token can have mint authority revoked, freeze authority revoked, LP locked — and still rug via Permanent Delegate burn.
Detection Pipeline: 4 Layers
Layer 1: On-Chain Extension Scanner (TypeScript)
Query the mint account directly and decode Token-2022 extensions:
import { Connection, PublicKey } from '@solana/web3.js';
import {
getMint, getExtensionData, ExtensionType,
TOKEN_2022_PROGRAM_ID
} from '@solana/spl-token';
interface TokenRiskReport {
mint: string;
hasPermanentDelegate: boolean;
delegateAddress: string | null;
delegateIsDeployer: boolean;
hasTransferFee: boolean;
transferFeeBps: number;
hasNonTransferableFlag: boolean;
riskScore: number; // 0-100
}
async function scanTokenExtensions(
connection: Connection,
mintAddress: PublicKey
): Promise<TokenRiskReport> {
const mintInfo = await getMint(
connection, mintAddress, 'confirmed', TOKEN_2022_PROGRAM_ID
);
let report: TokenRiskReport = {
mint: mintAddress.toBase58(),
hasPermanentDelegate: false,
delegateAddress: null,
delegateIsDeployer: false,
hasTransferFee: false,
transferFeeBps: 0,
hasNonTransferableFlag: false,
riskScore: 0,
};
// Check Permanent Delegate extension
try {
const delegateData = getExtensionData(
ExtensionType.PermanentDelegate,
mintInfo.tlvData
);
if (delegateData) {
report.hasPermanentDelegate = true;
report.delegateAddress = new PublicKey(delegateData.slice(0, 32)).toBase58();
report.riskScore += 60; // Highest single risk factor
// Check if delegate == mint authority (deployer pattern)
if (mintInfo.mintAuthority &&
report.delegateAddress === mintInfo.mintAuthority.toBase58()) {
report.delegateIsDeployer = true;
report.riskScore += 20;
}
}
} catch {}
// Check Transfer Fee extension (hidden fee = potential drain)
try {
const feeData = getExtensionData(
ExtensionType.TransferFeeConfig,
mintInfo.tlvData
);
if (feeData) {
report.hasTransferFee = true;
const feeBps = feeData.readUInt16LE(0);
report.transferFeeBps = feeBps;
if (feeBps > 500) report.riskScore += 15; // >5% fee is suspicious
}
} catch {}
return report;
}
Layer 2: Deployer Wallet Profiling (Python)
Scam factories reuse deployer wallets. Profile them:
import asyncio
from solders.pubkey import Pubkey
from solana.rpc.async_api import AsyncClient
async def profile_deployer(rpc_url: str, deployer: str) -> dict:
"""Score deployer wallet for scam factory patterns."""
client = AsyncClient(rpc_url)
deployer_key = Pubkey.from_string(deployer)
# Get all token mints created by this wallet
sigs = await client.get_signatures_for_address(deployer_key, limit=200)
token_mints = []
burn_txs = 0
lp_creates = 0
for sig_info in sigs.value:
tx = await client.get_transaction(
sig_info.signature, max_supported_transaction_version=0
)
if not tx.value:
continue
logs = tx.value.transaction.meta.log_messages or []
for log in logs:
if "InitializeMint2" in log:
token_mints.append(sig_info.signature)
if "Burn" in log and "PermanentDelegate" in str(logs):
burn_txs += 1
if "InitializePool" in log or "initialize" in log.lower():
lp_creates += 1
risk_indicators = {
"deployer": deployer,
"tokens_created": len(token_mints),
"burn_via_delegate": burn_txs,
"lp_pools_created": lp_creates,
"is_factory": len(token_mints) > 5 and burn_txs > 0,
"factory_score": min(100, len(token_mints) * 8 + burn_txs * 15),
}
await client.close()
return risk_indicators
Layer 3: Real-Time Burn Monitor (TypeScript + WebSocket)
Catch Permanent Delegate burns as they happen:
import { Connection, PublicKey, LogsFilter } from '@solana/web3.js';
function monitorDelegateBurns(rpcWsUrl: string) {
const connection = new Connection(rpcWsUrl, 'confirmed');
// Subscribe to Token-2022 program logs
const TOKEN_2022 = new PublicKey(
'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
);
connection.onLogs(TOKEN_2022, (logs) => {
const logStr = logs.logs.join('\n');
// Detect Burn instruction where signer != token owner
if (logStr.includes('Instruction: BurnChecked') ||
logStr.includes('Instruction: Burn')) {
// Parse accounts from the instruction
// Account[0] = token account (victim)
// Account[1] = mint
// Account[2] = authority (should be owner, but delegate if scam)
console.log(`⚠️ DELEGATE BURN DETECTED`);
console.log(` TX: ${logs.signature}`);
console.log(` Slot: ${logs.context?.slot}`);
// Alert pipeline: webhook, Telegram bot, or Forta agent
alertWebhook({
type: 'permanent_delegate_burn',
signature: logs.signature,
timestamp: Date.now(),
logs: logs.logs,
});
}
}, 'confirmed');
console.log('🔍 Monitoring Token-2022 burns via Permanent Delegate...');
}
async function alertWebhook(data: any) {
await fetch(process.env.ALERT_WEBHOOK!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
}
Layer 4: Pre-Trade Safety Gate (Anchor Program)
Enforce on-chain — reject swaps involving tokens with Permanent Delegate:
use anchor_lang::prelude::*;
use spl_token_2022::extension::{BaseStateWithExtensions, StateWithExtensions};
use spl_token_2022::state::Mint as Mint2022;
/// Verifies a Token-2022 mint has no dangerous extensions before allowing a swap
pub fn verify_safe_token(mint_info: &AccountInfo) -> Result<()> {
let mint_data = mint_info.try_borrow_data()?;
// Parse as Token-2022 mint with extensions
let mint_state = StateWithExtensions::<Mint2022>::unpack(&mint_data)
.map_err(|_| error!(ErrorCode::InvalidMint))?;
// CRITICAL: Reject tokens with Permanent Delegate
if mint_state.get_extension::<spl_token_2022::extension::permanent_delegate::PermanentDelegate>().is_ok() {
return Err(error!(ErrorCode::DangerousPermanentDelegate));
}
// Also flag: TransferFee > 10% (hidden fee drain)
if let Ok(fee_config) = mint_state
.get_extension::<spl_token_2022::extension::transfer_fee::TransferFeeConfig>()
{
let fee_bps: u16 = fee_config.newer_transfer_fee.transfer_fee_basis_points.into();
if fee_bps > 1000 {
return Err(error!(ErrorCode::ExcessiveTransferFee));
}
}
Ok(())
}
#[error_code]
pub enum ErrorCode {
#[msg("Token has Permanent Delegate extension — potential burn rug")]
DangerousPermanentDelegate,
#[msg("Token has >10% transfer fee — potential hidden drain")]
ExcessiveTransferFee,
#[msg("Invalid mint account")]
InvalidMint,
}
CI/CD Integration: GitHub Actions
Add this to your Solana project's CI pipeline:
name: Token Extension Security Scan
on:
pull_request:
paths: ['programs/**', 'tests/**']
jobs:
extension-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for Permanent Delegate usage
run: |
echo "🔍 Scanning for Permanent Delegate patterns..."
# Flag any code that initializes PermanentDelegate extension
if grep -rn "PermanentDelegate\|permanent_delegate" programs/; then
echo "⚠️ WARNING: PermanentDelegate extension detected"
echo "Review required: ensure delegate authority has proper governance"
# Don't auto-fail — legitimate compliance use exists
echo "::warning::PermanentDelegate extension found — manual review required"
fi
# Flag unconditional burn patterns (no multisig/timelock)
if grep -rn "burn_checked\|BurnChecked" programs/ | grep -v "timelock\|governance\|multisig"; then
echo "⚠️ Burn instruction without governance guard detected"
fi
- name: Run Anchor tests with extension checks
run: |
anchor test 2>&1 | tee test-output.log
# Verify tests cover extension validation
if ! grep -q "DangerousPermanentDelegate\|verify_safe_token" test-output.log; then
echo "::warning::No extension safety tests detected in test suite"
fi
10-Point Token-2022 Extension Security Checklist
Before interacting with any Token-2022 token:
| # | Check | Tool |
|---|---|---|
| 1 | Permanent Delegate exists? If yes → who controls it? | spl-token display <mint> |
| 2 | Delegate == deployer wallet? (rug factory pattern) | On-chain scanner |
| 3 | Transfer Fee > 5%? (hidden drain) | Extension decoder |
| 4 | Transfer Hook points to unverified program? | getExtensionData() |
| 5 | Deployer created >5 tokens in past 7 days? (factory) | Wallet profiler |
| 6 | Any Permanent Delegate burns in token history? | Transaction scanner |
| 7 | LP locked? (not sufficient alone — check #1 first) | LP lock checker |
| 8 | Mint authority revoked? (still rugable via delegate) | Mint account data |
| 9 | Non-Transferable flag set? (honeypot variant) | Extension decoder |
| 10 | Token age < 24h + high volume? (pump phase) | DEX analytics |
Critical insight: Checks 7 and 8 alone are not sufficient. A token can have LP locked and mint authority revoked while still being a rug via Permanent Delegate burn. Always start with check #1.
The Broader Pattern: Extension Abuse Beyond Permanent Delegate
Token-2022's extension model creates a new attack surface taxonomy:
- Permanent Delegate → unauthorized burn/transfer (this article)
- Transfer Fee → hidden tax up to 100% on every transfer
- Transfer Hook → arbitrary program execution on every transfer (can revert sells = honeypot)
- Non-Transferable → tokens that can never be sold (lock-in scam)
- Confidential Transfer → obscured amounts that hide whale dumps
Each extension was designed for legitimate use. Each has been weaponized. The security community needs extension-aware tooling as a first-class primitive, not an afterthought.
Conclusion
The Permanent Delegate burn scam isn't a bug — it's a feature being weaponized at industrial scale. In Q1 2026 alone, conservative estimates put losses from Token-2022 extension abuse at $50M+ across thousands of individual victims.
The defense isn't complicated: scan extensions before you swap. The four-layer pipeline above — on-chain scanner, deployer profiler, real-time monitor, and on-chain safety gate — covers detection from pre-trade to post-trade.
Every Solana DEX aggregator, wallet, and trading bot should be checking Token-2022 extensions by default. Until they do, the factory keeps running.
This is part of the DeFi Security Research series. Code examples are educational — audit thoroughly before production use.
Top comments (0)