# solana-token-extensions-security

Use when reviewing Solana or Anchor code that interacts with Token-2022 mints, token accounts, vaults, escrows, AMMs, lending markets, staking systems, or bridges. Focus on finding security bugs caused by Token-2022 mint extensions, account extensions, authority misuse, unsafe CPI assumptions, incorrect accounting, and missing validation of token state, mint provenance, transfer hooks, transfer fees, frozen defaults, delegates, close-and-reinitialize risk, and confidential-transfer edge cases.

- **Kind:** skill
- **Source:** https://github.com/zzzuhaibmohd/solana-token-extensions-security
- **Page:** https://forefy.com/skills/64fd26f7-e9a6-4f88-bf52-cb8cddef1a40
- **API (JSON + files):** https://forefy.com/api/skills/64fd26f7-e9a6-4f88-bf52-cb8cddef1a40

---

## README.md

# Solana Token-2022 Security Skill

A compact audit pack for reviewing Solana programs that touch Token-2022 mints, token accounts, vaults, escrows, staking flows, AMMs, and bridges.

Token-2022 changes the rules around:
- transfer fees
- transfer hooks
- permanent delegates
- mint close authority
- memo-required transfers
- default frozen accounts
- mint/account sizing
- metadata and group identity
- confidential balances
- wrapped SOL identity

## What’s Inside

- `SKILL.md` - main review workflow and heuristics
- `references/token-2022-patterns.md` - extension patterns and real issue classes
- `references/finding-templates.md` - report template, confidence matrix, Alice/Bob PoC framing

## Quick Start

```bash
git clone https://github.com/zzzuhaibmohd/solana-token-extensions-security.git
cd solana-token-extensions-security
```

Open these files first:
- [`SKILL.md`](./SKILL.md)
- [`references/token-2022-patterns.md`](./references/token-2022-patterns.md)
- [`references/finding-templates.md`](./references/finding-templates.md)

## How to Use

### Claude Code

Use this repo as a review playbook and ask Claude to audit the target project with a Token-2022 lens.

Example:

```text
Audit this Solana codebase using SKILL.md.
Look for Token-2022 extension bugs, incorrect accounting, unsafe CPI assumptions, and mint/reinitialize risks.
Return findings with severity, confidence score, evidence, Alice/Bob scenario, exploit path, and fix.
```

### Cursor

Keep `SKILL.md` open while reviewing the target codebase in Cursor, then search for the high-signal terms and compare behavior against `references/token-2022-patterns.md`.

### Codex

Load `SKILL.md` into context, use the issue bank while triaging, and format findings with `references/finding-templates.md`.

## Reporting Style

Every finding should include:
- Severity
- Confidence
- Confidence Score `0.0` to `1.0`
- Evidence
- Alice/Bob Scenario
- Exploit Path
- Fix

## Parallel Review

For bigger audits, split the work into parallel passes:
- transfer flows, fees, hooks, and memo constraints
- mint lifecycle, sizing, close-and-reinitialize, and authority model
- metadata, group identity, WSOL identity, program IDs, and interface selection
- vault, escrow, staking, and live balance reconciliation
- manual CPI wrappers and `remaining_accounts` forwarding

## Contribute

Open to contributors. If you find a new pattern:
1. Add it to `references/token-2022-patterns.md`
2. Add a report-ready line to `references/finding-templates.md`
3. Add a short heuristic to `SKILL.md` if it’s broadly useful

Suggestions and improvements are welcome, especially if they generalize well across different Solana protocols.

## References

Built from:
- [Token-2022 Security Best Practices, Part 1](https://blog.offside.io/p/token-2022-security-best-practices-part-1)
- [Token-2022 Security Best Practices, Part 2](https://blog.offside.io/p/token-2022-security-best-practices-part-2)
- [Neodyme: Token-2022 TL;DR](https://neodyme.io/en/blog/token-2022/#tldr)
- [0xFrankCastle Token-2022 audit thread](https://x.com/0xcastle_chain/status/2031497044775366770)
- [RareSkills Solana Tutorial (60 Days)](https://rareskills.io/solana-tutorial)

## SKILL.md

---
name: solana-token-extensions-security
description: Use when reviewing Solana or Anchor code that interacts with Token-2022 mints, token accounts, vaults, escrows, AMMs, lending markets, staking systems, or bridges. Focus on finding security bugs caused by Token-2022 mint extensions, account extensions, authority misuse, unsafe CPI assumptions, incorrect accounting, and missing validation of token state, mint provenance, transfer hooks, transfer fees, frozen defaults, delegates, close-and-reinitialize risk, and confidential-transfer edge cases.
---

# Solana Token-2022 Security Review

Use this skill when auditing Solana programs that accept, create, custody, or transfer Token-2022 assets.

Read [token-2022-patterns.md](references/token-2022-patterns.md) when you need extension-specific exploit ideas, edge cases, or review prompts.

Read [finding-templates.md](references/finding-templates.md) when writing findings, triaging severity, or converting review notes into clean report language.

Use the issue bank in [token-2022-patterns.md](references/token-2022-patterns.md) to map real audit findings to recurring Token-2022 failure modes:
- fee accounting drift on transfer-fee mints
- nominal credit vs spendable balance mismatches
- token-account mint / authority binding mistakes
- permanent-delegate vault custody breaks
- transfer-hook integration gaps and missing extra accounts
- remaining-accounts forwarding gaps in manual CPI wrappers
- mint-extension space is computed before all required mint extensions are added
- mixed token-program CPI wiring reuses one token program across multiple CPI legs
- confidential proof validation truncates after the expected prefix and ignores unused commitments

Use the confidence matrix in [finding-templates.md](references/finding-templates.md) to record how sure you are about each finding separately from severity.

When adding or writing findings, generalize the bug class whenever possible:
- describe the reusable Token-2022 failure mode first
- use protocol-specific examples only as illustrations
- only keep protocol-specific wording when the bug truly depends on that architecture

For larger audits, split the review into parallel passes when possible:
- one pass for transfer flows, accounting, fees, hooks, and memo constraints
- one pass for mint lifecycle, extension sizing, close-and-reinitialize risk, and authority model
- one pass for metadata, group, WSOL identity, program IDs, and interface-selection ambiguity
- one pass for vault / escrow / staking semantics and live balance reconciliation
- one pass for CPI wiring that spans both SPL Token and Token-2022 accounts or multiple token programs in the same instruction
- one pass for confidential-proof validation and commitment extraction bugs
- one pass for manual CPI wrappers that may need `remaining_accounts` forwarding

Assume the target may be vulnerable whenever it:
- trusts mint/account state without verifying extensions
- assumes all SPL-like tokens behave like classic SPL Token
- assumes transfers are synchronous, full-amount, transferable, unfrozen, or memo-free
- trusts mint addresses without considering close-and-reinitialize history
- treats token balances as invariant despite permanent delegates, mint authorities, or seizure-style controls
- hardcodes token-account size, rent, or closeability assumptions from classic SPL Token

Token-2022 token accounts keep the classic SPL Token account layout and append extension data. Treat every token account as potentially carrying extra rules that affect transferability, closure, ownership, or confidentiality.

Token-2022 mint accounts also append extension data. Treat every mint as potentially carrying extra rules that affect supply, fees, transfer policy, account state, identity, provenance, or group membership.

Mint extensions are fixed at creation time. Plan the full extension set up front, and respect any dependency constraints between mint extensions before initialization succeeds.

Permanent delegate on a mint is a high-risk authority. If present, it can transfer or burn from any token account for that mint, and transfer paths may authorize it automatically.

Interest-bearing mints use a fixed timestamp-based formula for UI conversions. If a project expects a different interest model, or if it relies on slot-based or custom rate calculations, this extension is not a drop-in fit.

Transfer-fee mints need explicit accounting for net received amounts, fee rounding, fee configuration delay, and withheld-fee harvesting. Do not treat `calculate_fee` and `calculate_inverse_fee` as strict inverses, and do not assume `withheld_amount` is real-time without harvesting.

Be careful with wrapped SOL. SPL Token WSOL and Token-2022 WSOL use different mint addresses, so contracts that special-case WSOL should distinguish them explicitly and avoid treating the Token-2022 WSOL as the canonical one by default.

SPL Token and Token-2022 are separate programs with different program IDs. Any code that uses token-program SDK helpers or CPIs must make the target program explicit instead of relying on library defaults.

Before auditing a contract, decide whether it is meant to support Token-2022. If Token-2022 support is intended, `anchor_spl::token_interface` is the right path; if not, prefer classic SPL token types and avoid accidental ambiguity from interface-based helpers.

## Review Goal

Find places where protocol assumptions and Token-2022 behavior diverge.

Prioritize:
1. Fund loss
2. Bypass of protocol restrictions
3. Incorrect accounting
4. Permanent DoS on vaults / escrows / user flows
5. Trust-model mismatches that admins or mint authorities can abuse

## Core Workflow

1. Identify every place the program:
   - accepts a mint
   - creates token accounts
   - transfers tokens
   - reads balances
   - closes token accounts
   - relies on owner, delegate, freeze, or mint authority assumptions
2. Determine whether the code uses classic SPL Token or Token-2022.
3. Enumerate the protocol assumptions:
   - exact amount received
   - token always transferable
   - token account immediately usable after creation
   - token account owner immutable or meaningful
   - mint config stable forever
   - mint extensions can be added or changed later without redesigning initialization
   - token-account size is fixed after creation
   - no third party can drain or burn vault funds
   - transfers only execute local logic
   - `amount == 0` is sufficient for token-account closure
   - SPL token account size / rent values still apply
   - reallocation is a rare edge case rather than a normal lifecycle step
4. Try to falsify each assumption using Token-2022 extensions.
5. Report the issue in exploit terms:
   - attacker setup
   - violating extension behavior
   - vulnerable assumption
   - user impact
   - minimal fix

## Fast Triage Questions

Ask these immediately during review:
- Can the protocol accept arbitrary mints?
- Does it rely on vault balances always matching internal accounting?
- Does it assume `transfer(amount)` means recipient received `amount`?
- Does it assume a newly created token account is usable immediately?
- Does it ever close and recreate mints, or trust mints created externally?
- Does it assume token accounts are normal ATAs with standard behavior?
- Does it derive expected ATAs with the correct token program id?
- Does it call into token transfers without handling hooks, memos, fees, freezes, or CPI restrictions?
- Does it use a single vault for tokens whose mint authorities can seize, burn, or drain balances?
- Does it hardcode token account rent, account size, or closure conditions?
- Does it still call plain `transfer` instead of `transfer_checked` or `transfer_checked_with_fee`?
- Does any keeper, relayer, or backend create token accounts for users using user-influenced extension space?
- Does mint initialization assume extensions can be added later?
- Does any CPI path reuse one token-program account across multiple CPI legs in the same instruction?
- Does any manual CPI wrapper hardcode `remaining_accounts_info` to `None` or otherwise drop extra account metas?
- Does the code separately validate token-account mint, owner, and authority/delegate relationships?
- Does it assume ATA-only behavior when generic token accounts are possible?
- Does it accept freeze or close authority without an explicit protocol policy?

If the answer to any is yes, inspect Token-2022 extension interactions before trusting the design.

## High-Signal Search Patterns

Search for these first:
- `transfer`
- `transfer_checked`
- `mint_to`
- `burn`
- `close_account`
- `approve`
- `set_authority`
- `freeze_account`
- `thaw_account`
- `InterfaceAccount<'info, Mint>`
- `InterfaceAccount<'info, TokenAccount>`
- `TokenInterface`
- `token::mint =`
- `associated_token`
- `spl_token_2022`
- `withheld_amount`
- `StateWithExtensions`
- `BaseStateWithExtensions`
- `closable()`
- `calculate_fee`
- `calculate_inverse_fee`
- `getMinimumBalanceForRentExemptAccountWithExtensions`
- `MintRequiredForTransfer`
- `anchor_spl::token::transfer`
- `transfer_checked_with_fee`
- `get_associated_token_address`
- `get_associated_token_address_with_program_id`
- `165`
- `reallocate`
- `createReallocateInstruction`
- `token_program_base`
- `token_program`
- `So11111111111111111111111111111111111111112`
- `9pan9bMn5HatX4EJdBwg9VgCa7Uz5HL8N1m5D3NdXejP`
- `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
- `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`
- `anchor_spl::token_interface`
- `anchor_spl::token::Token`
- `zip`
- `associated_token::token_program`

Also search for logic that:
- compares expected and actual token balances
- assumes balance deltas equal requested transfer amounts
- creates vaults or escrows and uses them immediately
- allowlists mints without provenance checks
- derives PDAs without including `mint.key()`
- hardcodes `165` bytes or static token-account rent
- closes accounts using only `amount == 0`
- creates token accounts for users from keeper or relayer infrastructure
- initializes the mint before initializing the intended extensions
- reallocates token accounts without deciding who pays the extra rent
- initializes a mint without satisfying mint-extension dependency constraints
- stores mint-derived state as if the mint can never be closed and recreated
- calls deprecated plain `transfer` on Token-2022 paths that need mint-aware transfer details
- special-cases WSOL without distinguishing SPL Token WSOL from Token-2022 WSOL
- relies on SDK defaults that point at the SPL Token program when Token-2022 is intended
- mixes `token_interface` helpers into a contract that is not meant to support Token-2022
- reuses a single token-program account across multiple CPI legs in the same instruction
- validates confidential proof commitments with a prefix-only comparison
- drops `remaining_accounts` from a CPI wrapper that may need extra account metas

## Extension Review Checklist

Use the extension checklist in [token-2022-patterns.md](references/token-2022-patterns.md) for detailed extension-by-extension review prompts.

At minimum, inspect mint-side extensions:
- non-transferable tokens
- transfer fees
- transfer hook
- confidential transfer
- confidential transfer fee
- mint close authority
- default account state
- interest-bearing tokens
- permanent delegate
- metadata pointer
- metadata
- group pointer
- group
- group member pointer
- group member

Issue-derived review patterns:
- Transfer-fee accounting drift: confirm the protocol books the net received amount, not the nominal transfer amount, and uses fee-aware helpers wherever rounding or withheld-fee state matters.
- Nominal credit vs spendable balance mismatch: confirm the protocol measures the receiver-side delta when the token behavior can reduce the credited amount.
- Permanent-delegate custody break: confirm the protocol trust-lists the mint and its delegate model before accepting deposits into shared vaults or reserves.
- Transfer-hook integration gap: confirm hook-enabled mints are supported end to end, including `remaining_accounts` / extra-account metas, mint-aware transfer instructions, and CPI forwarding where required.
- Mint-extension sizing failure: confirm the mint-space calculation happens only after every conditional extension has been added to the extension list.
- Multi-leg token-program CPI mismatch: confirm each CPI leg receives the correct token-program account and that one account is not being reused across token-program domains.
- Confidential proof validation truncation: confirm every expected commitment is checked and that any unused commitments are explicitly zeroed or rejected.
- Remaining-accounts forwarding gap: confirm the wrapper forwards extra account metas whenever the downstream CPI may need them.

For metadata, group, and member-style mint identity:
- anyone can create separate metadata, group, or group-member accounts and point them at a legitimate mint
- only the data referenced by the mint's pointer is authoritative
- data may live inside the mint extension or in a separate account, so verify the mutual reference relationship before trusting it

If the protocol special-cases WSOL:
- verify whether it means SPL Token WSOL or Token-2022 WSOL
- consider blacklisting the Token-2022 WSOL mint if the product only intends to support the canonical SPL WSOL

If the protocol uses token SDK helpers or CPIs:
- verify the program ID is explicitly Token-2022 when Token-2022 behavior is required
- verify helper defaults are not silently pointing at SPL Token

Check mint-extension dependency ordering before initialization:
- confidential transfer fee requires transfer fee and confidential transfer
- transfer fee plus confidential transfer requires confidential transfer fee

At minimum, inspect mint-close behavior:
- `MintCloseAuthority`
- supply must be zero before close
- protocol state that depends on a mint not being re-created at the same address
- `Metadata`
- `Group`
- `GroupMember`
- `MetadataPointer`
- `GroupPointer`
- `GroupMemberPointer`
- `PermanentDelegate`
- `InterestBearingConfig`
- `AmountToUiAmount`
- `UiAmountToAmount`
- `calculate_pre_fee_amount`
- `getTransferFeeConfig`
- `getEpochFee`
- `HarvestWithheldTokensToMint`

At minimum, inspect:
- immutable owner
- CPI guard
- required memo on transfer
- non-transferable tokens
- transfer fees
- transfer hook
- confidential transfer
- confidential transfer fee
- token-account reallocation
- mint close authority
- permanent delegate
- default account state
- memo transfer
- token account closure logic
- rent and account-size calculation
- `transfer` vs `transfer_checked`
- metadata pointer / group pointer
- metadata / group / group member pointer

## Common Vulnerability Themes

See [references/token-2022-patterns.md](references/token-2022-patterns.md) for the detailed reusable themes, break conditions, and fix directions.

Use this file as the compact audit workflow, and use the reference file for the full pattern catalog.

## Reporting Template

Use this structure for findings:

### Title

Short, exploit-focused bug title.

### Severity

High / Medium / Low / Info.

### Confidence

High / Medium / Low.

### Confidence Score

Use a numeric score from `0.0` to `1.0`.

### Evidence

- code path
- docs or issue reference
- local repro or test result

### Alice/Bob Scenario

- Alice: attacker or adversarial actor
- Bob: victim, protocol, or keeper
- show the smallest believable exploit or PoC path between Alice and Bob

### Preconditions

- which mint/account extensions are needed
- whether attacker controls a mint, token account, or recipient
- whether protocol accepts arbitrary Token-2022 assets

### Bug

Explain the exact protocol assumption that fails.

### Exploit Path

1. Attacker prepares mint / account / extension state.
2. Victim or protocol executes normal flow.
3. Extension behavior diverges from protocol assumption.
4. Funds are lost, accounting breaks, or the protocol is DoSed.

### Impact

State concrete effect:
- theft
- insolvency
- frozen funds
- bypassed KYC / fees / soulbound restrictions
- stuck closes

### Fix

State minimal fix and strongest fix.

Use severity and confidence separately:
- severity measures impact
- confidence measures certainty
- evidence explains why the confidence rating is justified
- confidence score should reflect how much of the exploit path is proven, not how severe the impact is
- Alice/Bob scenario should be short, concrete, and easy to convert into a PoC

## Strong Default Heuristics

Default to suspicion when:
- arbitrary user-supplied mints are accepted
- vault balances are trusted without reconciliation
- extension state is never inspected
- current mint data is treated as history
- token accounts are assumed to be standard ATAs
- token logic does not branch on Token-2022 features
- account creation or closing logic reuses classic SPL constants and assumptions

Default to lower severity when:
- the extension is cosmetic only and not used for auth or accounting
- the protocol explicitly trust-lists mints and authorities
- live balance checks and post-transfer reconciliation already exist

## Scope Notes

This skill is optimized for:
- Anchor programs
- Solana token vaults
- AMMs
- lending / margin / borrow-lend systems
- bridges
- staking systems
- escrow contracts
- NFT / collection logic using Token-2022 metadata or group features

When auditing a new extension, add it by preserving this format:
- what the extension changes
- broken protocol assumptions
- exploit shape
- impact
- minimal fix

When writing a new pattern, prefer reusable language over protocol-specific language:
- write the general bug class
- note the affected protocol type only as an example
- keep the heuristic useful across multiple Solana codebases whenever possible

### Theme: Program-Aware ATA Derivation

Red flag:
- protocol validates an expected associated token account using legacy SPL-only derivation while Token-2022 assets are in scope

Breaks under:
- Token-2022 associated-token-account derivation that depends on token program id
- finalize / claim / deposit flows that compare the wrong canonical ATA

## references

```

```

## references/finding-templates.md

# Finding Templates

Use this file to turn review notes into concise, high-signal findings.

When drafting a finding:
- generalize the bug class before mentioning a specific protocol
- use the protocol type as an example unless the bug is architecture-specific
- keep the wording reusable across AMMs, lending, staking, escrow, bridges, and other Solana programs whenever possible

## Minimal Finding Template

### Title

Use an exploit-focused title.

Examples:
- Transfer-fee mint causes escrow over-crediting
- Close-and-reinitialize mint bypasses extension-dependent safety assumptions
- Permanent delegate can externally drain protocol vault
- Default-frozen token account bricks escrow initialization

### Preconditions

State:
- required mint or account extensions
- attacker capabilities
- whether arbitrary mints or accounts are accepted

### Bug

Describe the protocol assumption that fails.

### Severity

Choose one:
- High: direct theft, drain, insolvency, or protocol-wide bypass
- Medium: user-specific loss, stuck flow, or recoverable accounting mismatch
- Low: compatibility issue or limited blast radius

### Confidence

Choose one:
- High: code path is direct and confirmed by docs, tests, or a repro
- Medium: code strongly suggests the issue, but one assumption still needs verification
- Low: issue is plausible but not yet sufficiently proven

### Confidence Score

Use a numeric score from `0.0` to `1.0`.

Suggested mapping:
- `0.9` to `1.0`: direct code proof plus docs or repro
- `0.6` to `0.8`: strong code evidence, one assumption left
- `0.3` to `0.5`: plausible but missing runtime proof
- `0.0` to `0.2`: speculative or weakly supported

### Evidence

State the proof quality:
- code-only
- docs-supported
- issue-supported
- local repro
- confirmed exploit path

### Alice/Bob Scenario

Write the smallest believable exploit story:
- Alice is the attacker or adversarial user
- Bob is the protocol, keeper, vault, or victim user
- describe the minimal action sequence that turns the bug into a PoC

### Exploit Path

1. Attacker prepares the mint, token account, or extension state.
2. Victim or protocol executes a normal flow.
3. Token-2022 behavior diverges from protocol expectations.
4. Funds are lost, policy is bypassed, or the flow is DoSed.

### Impact

Use concrete language:
- theft
- insolvency
- undercollateralization
- frozen funds
- stuck withdrawals
- bypassed compliance / fee / soulbound restrictions

### Fix

State:
- minimal fix
- stronger systemic fix

### PoC Notes

- keep the proof-of-concept path short
- use the Alice/Bob scenario to describe the steps in plain language
- include the exact extension(s) needed for the repro

## Confidence Matrix

Use this to classify certainty separately from severity:

### High Confidence
- direct code evidence
- docs or issue write-up confirm the behavior
- local repro or test demonstrates the path

### Medium Confidence
- strong code inference
- at least one assumption still needs validation
- likely exploitable, but not fully reproduced

### Low Confidence
- weak or indirect evidence
- depends on an inferred edge case
- more validation needed before filing as a finding

## Fast Parallel Review Plan

When auditing a larger codebase, split the work into parallel passes:

- Pass 1: transfer flows, fee math, hooks, memo constraints, and balance accounting
- Pass 2: mint lifecycle, extension sizing, close-and-reinitialize risk, and authority model
- Pass 3: metadata, group identity, WSOL identity, program IDs, and interface selection
- Pass 4: vault / escrow / staking semantics and live balance reconciliation

## Severity Heuristics

Higher severity:
- direct theft or drain
- global vault insolvency
- bypass of core collateral or accounting assumptions
- protocol-wide mint-acceptance bugs

Medium severity:
- user-specific loss
- stuck flows with practical exploitation
- accounting mismatch recoverable only with admin intervention

Lower severity:
- compatibility-only issues
- cosmetic identity confusion not used for auth or value decisions

## Review Writing Tips

- Name the broken assumption explicitly.
- Tie the bug to the extension behavior, not just to a code snippet.
- Describe the exploit path in operational terms.
- Prefer “attacker can” over “it might be possible.”
- Separate current-state checks from historical-trust issues.
- If the real issue is trust model, say that directly.

## Handy One-Liners

Use these when drafting findings:

- The protocol assumes the recipient receives the nominal transfer amount, which is false for fee-enabled Token-2022 mints.
- The protocol mixes Token-2022 fee helper semantics and assumes `calculate_fee` and `calculate_inverse_fee` are interchangeable, which can introduce persistent rounding loss.
- The protocol treats current mint configuration as proof of historical safety, which is invalid in the presence of mint close and reinitialization.
- The vault design assumes no external actor can mutate token balances, which is false when the mint exposes a permanent delegate or equivalent privileged authority.
- The flow assumes newly created token accounts are immediately usable, which is false for mints with default frozen account state.
- The transfer path assumes token movement is side-effect free, which is false for hook-enabled or memo-constrained Token-2022 assets.
- The protocol assumes classic SPL token-account size, rent, or closeability rules still apply, which is false for extension-bearing Token-2022 accounts.
- The protocol uses plain `transfer` in a Token-2022 path that requires mint-aware transfer instructions, causing extension-specific transfer failure.
- The protocol uses plain `transfer` where Token-2022 requires mint-aware details, so hook or fee-bearing mints can throw `MintRequiredForTransfer`.
- The protocol assumes Token-2022 closeability is equivalent to `amount == 0`, which is false once transfer-fee, confidential-transfer, or CPI-guard extensions are active.
- The protocol assumes token-account rent and size are static, which is false for extension-bearing Token-2022 accounts and can create correctness or keeper-loss bugs.
- The mint initialization flow assumes extensions can be added later, which is false for Token-2022 mint extensions and often leads to unsafe redesign patterns.
- The protocol ignores the CPI Guard destination-owner rule on close, which can make a larger CPI flow revert even when the account amount is zero.
- The protocol treats token-account size as fixed after creation, which is false once reallocation-capable account extensions are introduced.
- The mint initialization flow ignores extension dependency constraints, which can make valid-looking mint setups fail at creation time.
- The protocol stores mint-derived state as if the mint can never be closed and recreated, which is false once `MintCloseAuthority` exists.
- The protocol treats wrapped SOL as a single canonical mint, which is false because SPL Token WSOL and Token-2022 WSOL use different mint addresses.
- The protocol relies on token SDK defaults for program ID selection, which can silently route Token-2022 logic to the SPL Token program.
- The protocol uses `token_interface` without explicitly intending to support Token-2022, which can create ambiguous behavior in an SPL-only contract.
- The protocol trusts separately created metadata, group, or member accounts without verifying the mint's authoritative pointer, so spoofed identity data can be accepted.
- The protocol accepts a mint with `PermanentDelegate` without defining or monitoring delegate policy, so any delegate-authorized transfer can drain protocol funds.
- The protocol treats interest-bearing UI conversions as authoritative accounting, which is unsafe because the extension is timestamp-based and intended for UI representation.
- The protocol assumes transfer-fee helper calculations are exact inverses, which can create 1-unit rounding mismatches and stale-withheld accounting.
- The protocol assumes transfer-fee configuration changes are immediate, which is false because fee updates take effect only after the epoch delay.
- The protocol assumes withheld fees are real-time without harvest, which is false because `withheld_amount` is only synchronized when harvested to the mint.
- The protocol records nominal transfer amounts for a fee-bearing mint instead of net received amounts, which slowly drains vault or reserve accounting.
- The protocol records nominal transfer amounts as spendable balance, which can make later withdrawals fail when the receiver-side delta is smaller than expected.
- The protocol fails to bind token account mint, owner, and authority separately, which can route unrelated tokens or accept the wrong signer relationship.
- The protocol accepts a mint with `PermanentDelegate` without trust-listing or monitoring the delegate model, so a third party can drain protocol custody.
- The protocol accepts hook-enabled transfers without forwarding extra accounts or mint-aware transfer details, so the transfer hook can fail and DoS the flow.
- The protocol computes mint account space before appending all required extensions, so metadata-enabled mint creation fails at runtime because the account is undersized.
- The protocol reuses a single token-program account across multiple CPI legs, so mixed SPL Token / Token-2022 flows fail or route through the wrong program.
- The protocol validates confidential proof commitments with a length-limited comparison, so extra commitments can be ignored unless they are explicitly required to be zero.
- The protocol drops `remaining_accounts` from a manual CPI wrapper, so hook-enabled downstream instructions can fail even though the wrapper appears to support the token flow.
- The protocol derives expected associated token accounts with legacy SPL-only logic, so valid Token-2022 ATAs are rejected when the token program differs.

## references/token-2022-patterns.md

# Token-2022 Patterns

Use this file when you want extension-specific exploit ideas, broken assumptions, and fix directions during review.

Token-2022 token accounts are classic SPL accounts plus extension data. On the account side, the recurring extensions you should expect are:
- immutable owner
- CPI guard
- required memo on transfer
- non-transferable
- transfer fees
- transfer hook
- confidential transfer
- confidential transfer fee

Token-2022 mint accounts are classic SPL mints plus extension data. On the mint side, the recurring extensions you should expect are:
- non-transferable tokens
- transfer fees
- transfer hook
- confidential transfer
- confidential transfer fee
- mint close authority
- default account state
- interest-bearing tokens
- permanent delegate
- metadata pointer
- metadata
- group pointer
- group
- group member pointer
- group member

Mint extensions are fixed at creation time. If a mint needs multiple extensions, make sure you satisfy any dependency ordering before initialization.

Metadata, group, and member-style mint data can live either inside the mint extension area or in a separate account. Anyone can create a metadata, group, or group-member account and point it at a legitimate mint, so the mint-pointer relationship is the authoritative trust check.

Permanent delegate is a high-risk mint authority. If present, it can transfer or burn from any token account for that mint, and transfer paths may automatically authorize it without requiring the source account owner.

Interest-bearing mints use a fixed timestamp-based formula for UI conversions. If a project expects a different interest model, or if it relies on slot-based or custom rate calculations, this extension is not a drop-in fit.

Transfer-fee mints need explicit accounting for net received amounts, fee rounding, fee configuration delay, and withheld-fee harvesting. Do not treat `calculate_fee` and `calculate_inverse_fee` as strict inverses, and do not assume `withheld_amount` is real-time without harvesting.

Wrapped SOL has two common mint addresses in the ecosystem:
- SPL Token WSOL: `So11111111111111111111111111111111111111112`
- Token-2022 WSOL: `9pan9bMn5HatX4EJdBwg9VgCa7Uz5HL8N1m5D3NdXejP`

If a protocol special-cases WSOL, make sure it distinguishes these addresses explicitly. For products that only intend to support canonical SPL WSOL, blacklisting the Token-2022 WSOL mint can avoid ambiguity.

SPL Token and Token-2022 are separate programs with different program IDs:
- SPL Token: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
- Token-2022: `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`

When a helper or CPI can work with either program, do not rely on the SDK default. Make the intended program explicit.

Decide up front whether the contract supports Token-2022:
- if yes, prefer `anchor_spl::token_interface`
- if no, prefer classic SPL token types and avoid interface-based ambiguity

## Real Issue Patterns

These are the three audit patterns currently encoded from the issue bank:

### Transfer-Fee Accounting Drift

Look for:
- vaults or reserves that record the nominal transfer amount rather than the net received amount
- deposit, withdraw, stake, reward, or rebalance flows that assume fee-bearing transfers are 1:1
- code that ignores fee rounding, `calculate_pre_fee_amount`, or withheld-fee harvesting

Impact:
- slow insolvency
- user balance drift
- reserve accounting mismatch

Fix direction:
- book net received amounts
- use fee-aware transfer paths
- reconcile balances before and after sensitive flows

### Nominal Credit vs Spendable Balance Mismatch

Look for:
- vaults, reserves, or user-credit ledgers that record a nominal input amount instead of observed received balance
- deposit, exit, fee, or reward flows that trust the sender-side transfer amount without reconciling the receiver-side delta
- state variables that are later treated as fully spendable even though the underlying token account may have received less

Impact:
- accounting insolvency
- withdrawal DoS
- silent underpayment or over-crediting
- payout failure during later settlement or cleanup paths

Fix direction:
- measure the actual receiver-side balance delta
- store or credit the observed amount rather than the nominal input when the token behavior can differ
- cap later payouts by the real spendable balance and fail with an explicit insolvency error if needed

### Token Account Mint and Authority Binding

Look for:
- token-account inputs that are not constrained to the expected mint
- code that confuses token-account `owner`, transfer authority, and the token program itself
- CPI / transfer flows that accept arbitrary authority accounts without verifying they match the source owner or approved delegate
- protocols that silently assume ATA-only behavior even when generic token accounts are valid
- freeze / close authority that is present but not explicitly policy-checked

Impact:
- unrelated token routing
- failed or misdirected transfers
- stale delegate or authority abuse
- operational lockups from unexamined freeze or close policy

Fix direction:
- enforce mint consistency on every token account input
- validate authority binding against the source account owner or approved delegate
- require explicit protocol policy for freeze / close authority
- decide whether the protocol supports ATAs only or any valid token account, and enforce that choice consistently

### Permanent-Delegate Vault Custody Break

Look for:
- shared vaults or custody pools that accept arbitrary mints without trust-listing the delegate model
- reserve accounting that assumes no external authority can transfer or burn vault balances
- missing policy or monitoring for mints with permanent delegate enabled

Impact:
- direct vault drain
- reserve depletion
- protocol insolvency

Fix direction:
- trust-list mints and authorities
- explicitly model permanent-delegate power
- reject or isolate untrusted mints

### Transfer-Hook Integration Gap

Look for:
- hook-enabled mints passed through code paths that do not forward extra accounts
- hook-enabled mints passed through code paths that do not forward `remaining_accounts` / extra-account metas
- CPIs that omit mint-aware transfer details or rely on plain `transfer`
- hook paths that do not verify supported mints, transferring state, and token-account ownership

Impact:
- transfer failure
- missing policy enforcement
- integration-level DoS

Fix direction:
- forward the required extra accounts
- use mint-aware transfer instructions
- validate hook-supported mint sets explicitly

### Remaining-Accounts Forwarding Gap

Look for:
- manually constructed CPI instructions that hardcode `remaining_accounts_info` to `None`
- wrapper functions that never forward `remaining_accounts` into downstream CPI calls
- token-hook-aware flows that support some mints but drop the extra account payload required by hook-enabled paths

Impact:
- CPI failure when downstream programs require extra accounts
- inability to support hook-enabled or extra-account-driven token flows
- exit, collect, reward, or settlement paths becoming unusable for compatible mints

Fix direction:
- forward `remaining_accounts` whenever the downstream program may need extra account metas
- treat hook-aware CPIs as data-driven, not fixed-arity, wrappers
- test the wrapper with at least one hook-enabled mint and one no-hook mint

### Mint Extension Sizing Failure

Look for:
- mint size computed before conditional extensions are appended to the extension list
- `create_account` / mint creation paths that size the account from an empty or incomplete extension vector
- extension initialization CPIs executed after the mint was created with insufficient space

Impact:
- create-time failure
- metadata-enabled or extension-enabled flows become unavailable
- full DoS on mint creation or feature enablement paths

Fix direction:
- construct the full extension list first
- calculate mint size only after all conditional extensions are present
- size the mint for the final extension set before calling `create_account`

### Confidential Proof Validation Truncation

Look for:
- confidential mint, burn, or transfer validation code that uses `zip` or any length-limited comparison over proof commitments
- proof extraction routines that compare only the prefix of an expected commitment array
- unused proof commitments that are not explicitly required to be zero

Impact:
- malformed confidential-transfer proofs can evade full validation
- off-chain bugs in commitment assembly become harder to detect
- mint, burn, or transfer verification can accept inputs with hidden extra commitments

Fix direction:
- validate the full expected commitment set
- require all unused proof commitments to be zero
- do not rely on `zip` when extra elements must also be checked

### Multi-Leg Token-Program CPI Mismatch

Look for:
- CPI builders that reuse one token-program account across multiple CPI legs
- multi-asset instructions that accept both SPL Token and Token-2022 inputs but only thread one token program through
- duplicated `token_program` / `token_program_base` / equivalent accounts that drive separate CPI legs without a deliberate single-program policy
- any instruction that mixes token-program domains but does not pass the correct program account to each leg

Impact:
- CPI failure when different legs require different token programs
- protocol paths that work for one asset pair but break for mixed SPL Token / Token-2022 combinations
- integration DoS during redeem, withdraw, or settlement operations

Fix direction:
- pass the correct token-program account to each CPI leg explicitly
- only reuse one token-program account when the protocol truly enforces a single token-program family
- validate mixed-program paths during integration tests with at least one SPL Token and one Token-2022 asset pair

### Program-Aware ATA Derivation

Look for:
- expected associated token accounts derived with legacy SPL-only helpers in Token-2022-aware flows
- ATA validation that compares against a canonical address without threading the token program id into the derivation
- account constraints that assume associated token addresses are identical across SPL Token and Token-2022

Impact:
- valid Token-2022 ATA rejected as invalid
- finalize, claim, deposit, or settlement flows blocked by the wrong canonical account assumption
- compatibility DoS when Token-2022 assets are supported but not derived correctly

Fix direction:
- derive the expected ATA with a token-program-aware helper or explicit token program id
- pin the supported token-program family if only one is intended
- test the path separately with SPL Token and Token-2022 mints

## Transfer Fees

Look for:
- escrow or vault logic crediting the nominal amount instead of net received amount
- missing use of fee-aware transfer instructions
- code mixing `calculate_fee` and `calculate_inverse_fee` as if they are true inverses
- code assuming `calculate_pre_fee_amount` is interchangeable with fee estimates derived elsewhere
- close-account flows that ignore `withheld_amount`
- logic that assumes source and destination deltas match
- fee updates assumed to take effect immediately instead of after the epoch delay
- fee configs that leave `maximum_fee` unset or incorrectly defaulted
- code that assumes `TransferFeeConfig.withheld_amount` is synchronized with every transfer in real time

Impact:
- accounting mismatch
- undercollateralization
- stuck close flows
- silent long-tail rounding loss from 1-unit mismatches across volume
- fee bypass in edge cases involving stale accounts or reinitialized mints
- hidden 2-epoch delay where fee config changes have not yet taken effect
- inaccurate withheld-fee reporting until harvest is invoked

Fix direction:
- use fee-aware instructions where possible
- prefer `transfer_checked_with_fee` with the exact expected fee
- compare balances before and after transfer
- harvest withheld fees before close
- use `calculate_pre_fee_amount` only when the protocol really needs to invert from post-fee to pre-fee amounts
- query `getTransferFeeConfig` and `getEpochFee` when you need the effective fee schedule
- treat `HarvestWithheldTokensToMint` as the synchronization step for withheld fees

## Mint Close Authority

Look for:
- protocols trusting the current mint state without mint provenance
- “reject closeable mints” as the only protection
- extension-dependent logic on arbitrary external mints

Impact:
- close-and-reinitialize can bypass later extension assumptions
- old token accounts may remain valid but incompatible with new mint rules

Fix direction:
- rely on trusted mint registries / provenance
- do not treat current extension state as proof of historical safety

## Permanent Delegate

Look for:
- shared protocol vaults holding tokens from untrusted mints
- accounting that assumes no external party can transfer or burn vault funds
- insolvency-sensitive designs with no recheck of live balances
- protocols that do not explicitly define policy for mints with permanent delegate enabled
- systems that accept deposits before verifying the permanent delegate is trusted
- missing monitoring or alerting for mint authorities that can drain assets

Impact:
- external drain or burn of vault assets
- insolvency / bad debt / reserve mismatch
- unexpected losses from delegate-authorized transfers
- unmonitored mint authority actions

Fix direction:
- trust-list mints
- model external balance mutation as possible
- recheck balances before sensitive settlement
- define and document permanent-delegate policy explicitly
- monitor or alert on permanent-delegate activity where possible
- only accept assets from mints whose permanent delegate is trusted

## Default Account State

Look for:
- vault or escrow initialization that assumes newly created accounts are usable
- transfer or mint flows into accounts immediately after creation without state checks

Impact:
- frozen-by-default accounts can brick flows or trap funds

Fix direction:
- inspect account state after creation
- thaw if authorized and intended
- fail explicitly on frozen accounts

## Memo Transfer

Look for:
- transfers into arbitrary user token accounts with no memo support
- CPI transfer flows that do not prepend memo

Impact:
- recipient-specific DoS for incoming transfers

Fix direction:
- support `memo -> transfer` sequencing
- detect and surface memo-required failures clearly

## CPI Guard

Look for:
- protocols trying to move user tokens during CPI using owner authority alone
- designs that do not use delegate approval flow

Impact:
- transfer failures
- broken integrations

Fix direction:
- use delegate-based flows
- verify transfer success after CPI

## Transfer Hook

Look for:
- protocols assuming token transfers are pure token-program operations
- missing extra-account handling
- hook programs that fail to verify supported mints
- PDAs shared across different mints
- hook programs that do not verify `transferring` state
- hook logic that trusts token accounts without checking they belong to the passed mint

Impact:
- arbitrary policy bypass
- unauthorized PDA access
- cross-mint state collisions
- unexpected transfer failures / DoS

Fix direction:
- verify mint support
- verify transferring flags
- verify token account mint matches the mint account
- include mint in PDA seeds

## Token Account Closure

Look for:
- close logic that only checks `amount == 0`
- hand-rolled closability checks instead of extension-aware checks
- CPI close flows that ignore CPI Guard destination restrictions
- close flows that ignore `TransferFeeAmount.withheld_amount`
- close flows that ignore confidential pending or available balances
- close flows that ignore `ConfidentialTransferFeeAmount.withheld_amount`
- close flows that ignore the CPI Guard destination-owner rule in CPI contexts

Impact:
- stuck user exits
- stuck escrows or vault cleanup
- full instruction reverts when close is part of a larger flow

Fix direction:
- use each extension's `closable()` logic instead of hand-rolling checks
- inspect withheld balances and confidential balances explicitly if implementing custom close flows
- if closing via CPI, enforce the owner-destination rule before invoking the close instruction

## Reallocation

Look for:
- account extensions that are added after initial account creation
- reallocate flows that do not treat extra rent as a protocol cost decision
- create-reallocate helper usage that ignores `payer`
- backend flows that assume account size can never change after creation

Impact:
- unexpected rent loss
- account creation or extension enablement failure
- keeper or protocol overpaying when users control the extension set

Fix direction:
- make reallocation an explicit part of the account lifecycle
- decide who pays additional rent before invoking reallocation
- use extension-aware rent and size calculations at the time of reallocation

## transfer vs transfer_checked

Look for:
- `anchor_spl::token::transfer`
- Token-2022 flows using plain `transfer` instead of `transfer_checked`
- Token-2022 flows using plain `transfer` instead of `transfer_checked_with_fee`
- missing mint account or decimals in transfer paths
- code that ignores `MintRequiredForTransfer`
- call sites that do not provide the mint when the token requires hook or fee resolution

Impact:
- transfers fail with `MintRequiredForTransfer`
- integrations break only for extension-enabled tokens
- fee-bearing or hook-bearing transfers revert even when the code looks valid in classic SPL Token

Fix direction:
- use `anchor_spl::token_interface`
- use `transfer_checked` for Token-2022
- use `transfer_checked_with_fee` when fee-bearing tokens are supported
- prefer mint-aware transfer paths whenever the mint can carry `TransferHook` or `TransferFee` extensions

## Dynamic Rent and Account Size

Look for:
- hardcoded `165` byte token-account assumptions
- hardcoded rent values for token accounts
- backend or keeper flows paying for user-created extension accounts
- runtime account creation that ignores `getMinimumBalanceForRentExemptAccountWithExtensions`

Impact:
- account creation failure
- keeper or relayer overpayment
- DoS for extension-bearing account creation

Fix direction:
- compute rent dynamically with extension-aware helpers
- do not assume classic SPL token-account size
- avoid keeper-funded account creation when users control the extension space

## Mint Initialization Order

Look for:
- mint creation flows that initialize the base mint before all required extensions
- designs that expect mint extensions to be added after initialization
- backend code that allocates mint space as if it were a classic SPL mint
- mint extension combinations that ignore dependency constraints

Impact:
- extension setup failure
- incorrect mint layout
- redesign pressure that leads teams toward unsafe close-and-reinitialize workflows
- mint initialization reverting because a required companion extension was not enabled

Fix direction:
- decide the full extension set up front
- allocate extension-aware mint space before initialization
- initialize required extensions before initializing the base mint
- enforce mint-extension dependency ordering in the mint-creation flow

## Mint Close Authority

Look for:
- protocol state that assumes a mint address can never be closed and recreated
- mint-derived caches or registry entries that are not refreshed after close/recreate
- close flows that do not confirm mint supply is zero before close

Impact:
- stale mint-derived state
- inconsistent protocol metadata
- close-and-recreate history that invalidates trust assumptions

Fix direction:
- treat mint close as a provenance event
- refresh mint-derived state from the canonical mint account
- do not trust a mint address alone to imply stable history

## Group Pointer / Metadata Pointer

Look for:
- logic that treats pointer presence as sufficient proof of identity
- missing bidirectional verification
- group or group-member flows that trust only one side of the pointer relationship

Impact:
- spoofed identity
- fake collections
- bad allowlist decisions

Fix direction:
- verify mint points to metadata/group
- verify metadata/group points back to mint

## Mint Identity and Grouping

Look for:
- code that assumes metadata or group membership is cosmetic and never needs validation
- allowlist or collection logic that trusts only one side of the reference
- group-member validation that does not also verify the canonical mint or group account
- contracts that trust separately created metadata, group, or group-member accounts without verifying the mint's pointer
- code that fails to distinguish embedded mint-side metadata from separately created accounts

Impact:
- spoofed collection membership
- fake identity or provenance
- incorrect allowlist or gating decisions
- authoritative data confusion between embedded and external accounts

Fix direction:
- verify both directions of the mint-to-metadata and mint-to-group relationships
- treat metadata, group, and group-member extensions as identity inputs when used for auth or policy
- prefer the mint's pointer as the source of truth when external and embedded data disagree

## WSOL Identity

Look for:
- contracts that special-case WSOL without checking whether the mint is SPL Token or Token-2022
- DeFi logic that assumes one canonical wrapped SOL mint
- blacklist or allowlist logic that omits the Token-2022 WSOL mint

Impact:
- ambiguous asset handling
- incorrect routing or pricing assumptions
- unintended support for Token-2022 WSOL in products that only intend canonical SPL WSOL

Fix direction:
- explicitly compare against the exact WSOL mint addresses you support
- blacklist the Token-2022 WSOL mint when the product only supports SPL WSOL

## Program ID Selection

Look for:
- SDK helpers that default to the SPL Token program ID
- CPIs that omit the token program account or pass the wrong one
- code that assumes a shared interface implies shared program behavior

Impact:
- Token-2022 instructions routed to SPL Token
- `MintRequiredForTransfer` or extension-related failures
- subtle mismatches between intended and actual token-program behavior

Fix direction:
- explicitly set the token program ID at every CPI boundary
- treat SDK defaults as unsafe unless the product only supports SPL Token

## Interface Selection

Look for:
- contracts that import `anchor_spl::token_interface` without intending to support Token-2022
- code that mixes SPL-only assumptions with interface-based token abstractions
- audit targets that do not declare a token-program support policy up front

Impact:
- ambiguous program behavior
- accidental extension compatibility exposure
- surprising CPI or account-type mismatches

Fix direction:
- decide support policy before implementation
- use `token_interface` only when Token-2022 support is intended
- use `anchor_spl::token::Token` for SPL-only contracts

## Immutable Owner

Look for:
- protocols assuming all token accounts can have owner reassigned
- ATA logic that breaks when owner is immutable

Impact:
- compatibility issues
- mistaken recovery or admin flows

Fix direction:
- do not assume owner reassignment is available

## Non-Transferable

Look for:
- code assuming all accepted collateral or deposits can later be transferred out
- liquidation or withdrawal flows that require transferability

Impact:
- stuck assets
- broken exits

Fix direction:
- reject unsupported non-transferable mints or design around burn/close-only behavior

## Interest Bearing Mint

Look for:
- protocol logic using UI amounts instead of raw amounts
- projects that assume a non-timestamp interest formula
- systems that rely on strict precision between UI conversions and actual accounting
- logic that treats network timestamp drift as impossible or irrelevant

Impact:
- user-facing confusion
- incorrect display logic
- UI amount mismatch against expected interest model
- apparent balance drift when timestamps are unstable

Fix direction:
- use raw amounts for protocol accounting
- treat `AmountToUiAmount` and `UiAmountToAmount` as UI helpers, not as authoritative settlement math
- confirm the timestamp-based formula matches the project’s intended interest model before support is added

## Confidential Transfer / Confidential Transfer Fee

Look for:
- assumptions that public balances reflect spendable value
- logic ignoring pending confidential balances
- unsupported proof or account lifecycle in protocols that claim compatibility
- close or exit flows that assume public-balance semantics

Impact:
- broken accounting
- stuck user flows
- false assumptions around usable balance

Fix direction:
- distinguish public, confidential, and pending balances
- require explicit lifecycle support before claiming compatibility

## Cross-Cutting Patterns

### Exact-Amount Assumption

Red flag:
- protocol increments internal credit by requested transfer amount

Usually breaks under:
- transfer fees
- failed memo-required transfers
- hook-governed transfers
- fee rounding differences from mismatched fee helpers

### Immediate-Usability Assumption

Red flag:
- protocol creates a token account and immediately uses it without state checks

Usually breaks under:
- default frozen accounts
- extension-sensitive account setup

### Stable-Mint Assumption

Red flag:
- protocol allowlists by current mint state only

Usually breaks under:
- close-and-reinitialize

### Vault-Can’t-Be-Drained Assumption

Red flag:
- protocol treats live vault balance as impossible to mutate externally

Usually breaks under:
- permanent delegate
- trusted-authority seizure power

### Plain-Transfer Assumption

Red flag:
- protocol assumes transfer is only token movement with no side effects

Usually breaks under:
- transfer hook
- memo transfer
- CPI guard
- plain `transfer` used against Token-2022 extension-bearing accounts

### SPL-Compat Assumption

Red flag:
- protocol reuses classic SPL constants, rent values, or closability rules

Usually breaks under:
- extension-sized token accounts
- extension-specific close restrictions
- Token-2022 transfer requirements

