Skip to main content
Sigvex

Token-2022 Extension Interaction Hazards

Detects Token-2022 mints that enable extension combinations with undefined, contradictory, or insecure interactions.

Token-2022 Extension Interaction Hazards

Overview

Token-2022 (Token Extensions) lets a single mint or account combine multiple extensions. Each extension is well-defined on its own, but some pairs interact in ways that are undefined, economically exploitable, or directly contradictory. A mint that enables transfer fees alongside interest accrual, a transfer hook alongside confidential transfers, or a memo requirement alongside confidential transfers is asking two features to coexist whose semantics were never reconciled.

This detector identifies functions that initialize two or more extensions whose combination is known to be hazardous. It pairs each extension initialization against the others enabled in the same scope and reports any pair that matches a known dangerous combination, along with the specific risk that combination introduces.

Why This Is an Issue

The danger depends on the pair:

  • Transfer Fee + Interest Bearing. It is undefined whether fees apply to principal only or to principal plus accrued interest, and whether the fee is computed before or after accrual. Inconsistent handling produces wrong fee amounts and can let users avoid fees through interest manipulation.
  • Transfer Hook + Confidential Transfer. Confidential transfers hide amounts and participants behind zero-knowledge proofs, but a transfer hook receives plaintext transfer details. The hook program sees everything the confidential transfer was meant to hide, breaking the privacy guarantee and enabling de-anonymization or front-running. This is the most severe pairing the detector reports.
  • Permanent Delegate + Freeze Authority. A permanent delegate can move tokens without owner approval; a freeze authority can freeze accounts. Whether a permanent delegate can transfer out of a frozen account is ambiguous, and divergent implementations create privilege-escalation surprises.
  • Memo Required + Confidential Transfer. A memo requirement exposes transaction metadata for transparency, while confidential transfers exist to hide it. The two goals cancel out, defeating the confidentiality users expect.

In every case the program is shipping behavior that no specification pins down, which is fertile ground for economic exploits and broken privacy assumptions.

How to Resolve

Decide explicitly which extension combinations your program supports, and reject the hazardous ones at initialization rather than discovering the interaction in production.

use spl_token_2022::extension::{
    BaseStateWithExtensions, ExtensionType, StateWithExtensions,
};
use spl_token_2022::state::Mint;

// Before: a mint is accepted with whatever extensions it carries,
// including combinations whose interaction is undefined.
fn register_mint(ctx: Context<RegisterMint>) -> Result<()> {
    store_mint(&ctx)?;
    Ok(())
}

// After: enumerate the mint's extensions and reject known-hazardous pairs.
fn register_mint(ctx: Context<RegisterMint>) -> Result<()> {
    let data = ctx.accounts.mint.try_borrow_data()?;
    let state = StateWithExtensions::<Mint>::unpack(&data)?;
    let extensions = state.get_extension_types()?;

    let has = |e: ExtensionType| extensions.contains(&e);

    require!(
        !(has(ExtensionType::TransferHook) && has(ExtensionType::ConfidentialTransferMint)),
        RegistryError::HookBreaksConfidentiality
    );
    require!(
        !(has(ExtensionType::TransferFeeConfig) && has(ExtensionType::InterestBearingConfig)),
        RegistryError::UndefinedFeeInterestInteraction
    );
    drop(data);

    store_mint(&ctx)?;
    Ok(())
}

If a combination must be supported, document precisely how the interaction is resolved, add integration tests that exercise both extensions together, and warn users when a privacy or fee guarantee no longer holds.

Sample Sigvex Output

{
  "detector_id": "token-2022-extension-interactions",
  "severity": "critical",
  "confidence": 0.78,
  "description": "Mint has both transfer hook and confidential transfer extensions; the hook sees plaintext details of confidential transfers.",
  "location": {
    "function": "register_mint",
    "offset": 1
  }
}

Detection Methodology

Working from the reconstructed intermediate representation of each function recovered from the program binary, the detector examines each basic block independently:

  1. Extension collection. It identifies cross-program invocations whose instruction discriminator corresponds to a Token-2022 extension initialization and maps each to a known extension type (transfer fee, interest bearing, transfer hook, confidential transfer, permanent delegate, memo required, freeze authority).

  2. Pairwise hazard check. Blocks that initialize fewer than two extensions are skipped. For blocks with two or more, each newly seen extension is checked against the others already enabled in that block. When a pair matches an entry in the catalog of hazardous combinations, a finding is emitted carrying that combination’s severity, description, and remediation guidance.

Severity is per-combination rather than fixed for the detector: the transfer-hook-plus-confidential pairing is reported as critical, while the others range from medium to high. Confidence is reduced for administrative or initialization functions and for read-only contexts, where the interaction has less practical impact.

Limitations

The detector recognizes a curated set of hazardous pairs; combinations outside that set are not flagged even if a future analysis shows them to be unsafe. It reasons about extensions initialized within a single block, so combinations assembled across separate functions or transactions may be missed. Extension initializations whose discriminator is computed dynamically cannot be resolved statically. Because severity is attached to the specific pair, a single function can produce findings at different severities.

References