Skip to content
Security
Skill

/fungible-asset-security

Trigger FA_STANDARD flag detected (protocol uses FungibleAsset standard) - Used by Breadth agents, depth-token-flow

From plugin
plamen
276160 skills12 agents4 commands
Install
$ npx -y skills add PlamenTSV/plamen --skill fungible-asset-security --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/fungible-asset-security

Context preview

The summary Claude sees to decide when to auto-load this skill.

Trigger FA_STANDARD flag detected (protocol uses FungibleAsset standard) - Used by Breadth agents, depth-token-flow

SKILL.md

fungible-asset-security.SKILL.md
name: "fungible-asset-security"
description: "Trigger FA_STANDARD flag detected (protocol uses FungibleAsset standard) - Used by Breadth agents, depth-token-flow"

Skill: FUNGIBLE_ASSET_SECURITY

> **Trigger**: FA_STANDARD flag detected (protocol uses FungibleAsset standard) > **Used by**: Breadth agents, depth-token-flow > **Covers**: FungibleAsset metadata validation, zero-value exploitation, store ownership, dispatchable hooks, Ref safety, Coin-to-FA migration

Purpose

Audit FungibleAsset standard usage for Aptos-specific vulnerabilities. The FA standard introduces object-based token management with capabilities (MintRef, BurnRef, TransferRef, FreezeRef) and optional dispatchable hooks. Incorrect usage creates counterfeit token acceptance, forced transfers, reentrancy, and accounting mismatches.

Methodology

STEP 1: Metadata Validation Audit

For EVERY function that accepts a `FungibleAsset` parameter or reads from a `FungibleStore`:

| # | Function | Accepts FA/Reads Store | Validates Metadata? | Expected Metadata | Bypass Possible? | |---|----------|----------------------|--------------------|--------------------|-----------------| | 1 | {func} | FungibleAsset param | YES/NO | {expected_metadata_obj} | YES/NO |

**How metadata validation works**:

// CORRECT: validates the asset is the expected type
let metadata = fungible_asset::metadata(&fa);
assert!(metadata == expected_metadata, ERROR_WRONG_ASSET);

// VULNERABLE: no validation - accepts ANY FungibleAsset
public fun deposit(fa: FungibleAsset) {
    // Attacker can pass a worthless FA created from their own metadata
    fungible_asset::deposit(store, fa);
}

**MANDATORY SEARCH**: Grep all `.move` files for: 1. `FungibleAsset` in function signatures (parameters) 2. For each hit: trace whether `fungible_asset::metadata(&fa)` is called and compared 3. Functions that ONLY use `fungible_asset::amount(&fa)` without metadata check -> FLAG

**Severity**: Accepting unvalidated FungibleAsset = accepting counterfeit tokens. If the function credits the user or modifies protocol state based on the FA amount -> HIGH/CRITICAL.

STEP 2: Zero-Value Exploitation

Analyze zero-value FungibleAsset paths:

| # | Zero-Value Source | Code Path Triggered | State Modified? | Cleanup Correct? | |---|------------------|-------------------|----------------|-----------------| | 1 | `fungible_asset::zero(metadata)` | {trace what happens} | YES/NO | YES/NO | | 2 | Withdrawal of 0 amount | {trace} | YES/NO | YES/NO |

**Check for each**: 1. Can `fungible_asset::zero(metadata)` be used to trigger code paths that modify state? (e.g., register a user, set a flag, emit an event) 2. Does `fungible_asset::destroy_zero(fa)` clean up properly, or does it leave dangling state? 3. Can zero-value deposits/withdrawals:

  • Register a new FungibleStore where one shouldn't exist?
  • Trigger reward distribution checkpoints?
  • Bypass minimum deposit requirements (checked after or before deposit)?
  • Create entries in tracking data structures (SmartTable, vector)?

4. Does `amount == 0` get explicitly checked and rejected at entry points?

**Pattern**: Zero-value operations often bypass `amount > 0` checks that were assumed but never written, allowing state modifications without economic cost.

STEP 3: Store Creation and Ownership Analysis

Audit FungibleStore creation, ownership chains, and access control:

3a. Store Creation Inventory

| Store Type | Created By | Creation Permissionless? | Owner | Can Attacker Create? | |-----------|-----------|-------------------------|-------|---------------------| | Primary store | `primary_fungible_store::ensure_primary_store_exists()` | YES - anyone can create for any address | Address owner | YES (for any address) | | Custom store | `fungible_asset::create_store()` on ConstructorRef | Only during object construction | Object owner | Depends on who can construct |

**CRITICAL**: `primary_fungible_store::ensure_primary_store_exists(addr, metadata)` is permissionless. An attacker can create a primary store for ANY address for ANY metadata. If the protocol assumes a store's existence means the user has interacted with the protocol -> FINDING.

3b. Transitive Ownership

| Object A | Owns Object B | B Has FungibleStore | A Can Withdraw from B? | |----------|-------------|--------------------|-----------------------| | {object} | {child_object} | YES/NO | YES - via object ownership chain |

**Check**: If Object A owns Object B which owns a FungibleStore, the owner of Object A can withdraw from B's store through the ownership chain. Trace all object ownership hierarchies for unintended fund access paths.

3c. Store Address Confusion

| Function | Expects Store At | Actually Reads From | Match? | |----------|-----------------|--------------------|---------| | {func} | Protocol-controlled store | User-supplied address | VERIFY |

**Pattern**: Protocol calculates expected store address but user can supply a different store address. If the function doesn't verify the store belongs to the expected object/address -> FINDING.

STEP 4: Dispatchable Hook Analysis

If the protocol uses dispatchable FungibleAsset (custom `withdraw`, `deposit`, or `derived_balance` hooks):

4a. Hook Inventory

| Hook Type | Registered? | Implementation Module | Can Reenter? | Can Revert? | Can Manipulate? | |-----------|-------------|---------------------|-------------|------------|-----------------| | withdraw | YES/NO | {module::func} | ANALYZE | ANALYZE | ANALYZE | | deposit | YES/NO | {module::func} | ANALYZE | ANALYZE | ANALYZE | | derived_balance | YES/NO | {module::func} | ANALYZE | N/A | ANALYZE |

4b. Reentrancy via Hooks

For each registered hook: 1. Does the hook call back into the registering module's public functions? 2. Does the hook call into any other module that reads/writes shared state? 3. Is `#[module_lock]` applied to the registering module? (prevents indirect reentrancy but N

Read more
Ships withplamen

Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.

Get the whole plugin