agents/skills/aptos/token-flow-tracing/SKILL.md
Trigger Pattern BALANCE_DEPENDENT flag (required) - Inject Into Depth-token-flow, breadth agents
npx skillsauth add plamentsv/plamen token-flow-tracingInstall 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.
Trigger Pattern: BALANCE_DEPENDENT flag (required) Inject Into: Depth-token-flow, breadth agents Purpose: Trace all token flows through Aptos Move protocols using FungibleAsset and Coin<T> models, identifying accounting desync, unsolicited deposit vectors, type confusion, and dispatchable hook side effects
For every token the protocol handles:
Enumerate ALL asset types the protocol handles:
| Asset | Model | Type Parameter / Metadata | Decimals | Entry Modules | Exit Modules | |-------|-------|--------------------------|----------|---------------|-------------| | {name} | Coin<T> / FungibleAsset | {CoinType or metadata Object} | {decimals} | {list} | {list} |
Aptos dual token model:
CoinStore<T> resource at user address. Type parameter T identifies the coin.FungibleStore objects. Metadata object identifies the asset type.Where can tokens enter the protocol?
| Entry Path | Function | Asset Model | Accounting Updated? | Access Control |
|------------|----------|-------------|--------------------|--------------|
| Standard deposit | {deposit_fn} | {Coin/FA} | YES/NO | {who can call} |
| primary_fungible_store::deposit() | External | FA | NO (protocol unaware) | Permissionless |
| coin::deposit<T>() | External | Coin<T> | NO (protocol unaware) | Permissionless (if CoinStore registered) |
| Direct fungible_asset::deposit() | Via store ref | FA | NO (protocol unaware) | Requires FungibleStore reference |
| move_to<T>() | Internal | Resource | {depends} | Module only |
| Side-effect receipts | External call returns | {varies} | {depends} | {depends} |
Red flags:
object::generate_signer() or object::generate_extend_ref() which could allow external depositsWhere can tokens leave the protocol?
| Exit Path | Function | Asset Model | Accounting Updated? | Access Control |
|-----------|----------|-------------|--------------------|--------------|
| Standard withdraw | {withdraw_fn} | {Coin/FA} | YES/NO | {who can call} |
| primary_fungible_store::withdraw() | Via signer | FA | {depends} | Requires signer capability |
| coin::withdraw<T>() | Via signer | Coin<T> | {depends} | Requires signer |
| fungible_asset::withdraw() | Via store ref | FA | {depends} | Requires store &mut ref or TransferRef |
| Fee distribution | {fee_fn} | {varies} | YES/NO | {access} |
| Emergency withdraw | {emergency_fn} | {varies} | YES/NO | {admin} |
For each exit: does the tracked balance decrease BEFORE or AFTER the actual transfer? For each transfer call: can the source account be underfunded at execution time? (funds deployed externally, locked, or lent out → transfer reverts)
For each transfer function: can the sender and recipient be the same account/address? If YES: does a self-transfer update accounting state (fees credited, rewards claimed, snapshots updated, share ratios changed) without net token movement? Flag as FINDING.
For each asset type:
| Asset | Internal Tracking Variable | On-Chain Balance Query | Can Desync? | Desync Vector |
|-------|---------------------------|----------------------|-------------|---------------|
| {name} | {e.g., total_deposited in resource} | fungible_asset::balance(store) or coin::balance<T>(addr) | YES/NO | {if YES: how} |
Critical question: Does the protocol use internal accounting or direct on-chain balance queries?
total_deposited / total_assets resource -> SAFE from donation attacks IF consistently updatedfungible_asset::balance() or coin::balance<T>() directly -> DONATION ATTACK VECTOR -- attacker can inflate balance without protocol awarenessRed flags:
fungible_asset::balance(store) directlyCan tokens be deposited to the protocol without calling its deposit function?
If YES (most cases on Aptos):
| Vector | Asset Model | Protocol Aware? | Impact |
|--------|-------------|----------------|--------|
| primary_fungible_store::deposit(protocol_addr, fa) | FA | NO | {impact} |
| coin::deposit<T>(protocol_addr, coin) | Coin<T> | NO | {impact} |
| Direct transfer to object-owned store | FA | NO | {impact} |
For EVERY external token type the protocol holds, queries, or receives as side effects:
| Token Type | Can Deposit Unsolicited? | Accounting Distortion? | Share Inflation? | Threshold Manipulation? | Reward Dilution? | Fee Calculation Impact? | |------------|------------------------|----------------------|-----------------|----------------------|-----------------|----------------------| | {token_a} | YES/NO | YES/NO | YES/NO | YES/NO | YES/NO | YES/NO |
RULE: If ANY token type is unsolicited-depositable AND affects state -> analyze each consequence:
If NO:
Can the wrong asset type be used where another is expected?
| Check | Location | Status | Impact | |-------|----------|--------|--------| | FungibleAsset metadata validated on deposit? | {fn} | YES/NO | Wrong FA type accepted | | Coin<T> type parameter constrains to expected type? | {fn} | YES/NO (compile-time) | N/A for Coin (type-safe) | | Paired Coin/FA confusion? | {fn} | YES/NO | Same asset counted twice | | Metadata address hardcoded or validated? | {fn} | {which} | Spoofed metadata |
Aptos-specific type confusion vectors:
If any token in scope uses the Aptos FungibleAsset dispatchable hooks (DispatchFunctionStore):
| Token | Hook Type | Hook Function | Side Effect | Protocol Handles? | |-------|-----------|--------------|-------------|-------------------| | {token} | deposit | {module::fn} | {effect} | YES/NO | | {token} | withdraw | {module::fn} | {effect} | YES/NO | | {token} | derived_balance | {module::fn} | {effect} | YES/NO |
Analysis questions:
is_dispatchable() before interacting with tokens?What happens with zero-amount deposits/withdrawals?
| Operation | Zero Amount Behavior | Accounting Impact | Shares Issued/Burned? | |-----------|---------------------|-------------------|----------------------| | deposit(0) | {aborts/succeeds} | {state change?} | {0 shares / abort?} | | withdraw(0) | {aborts/succeeds} | {state change?} | {0 shares burned?} | | transfer(0) | {aborts/succeeds} | {state change?} | N/A |
Red flags:
For protocols handling multiple token types:
| Token A | Token B | Interaction | Can A Affect B? | Impact | |---------|---------|-------------|----------------|--------| | {tokenA} | {tokenB} | {rate dependency / collateral relationship / swap} | YES/NO | {impact} |
For each token identified:
| Token | Entry Points | Exit Points | Tracking Var | Direct Balance Query Used? | Unsolicited Possible? | |-------|--------------|-------------|--------------|---------------------------|----------------------| | {name} | {list} | {list} | {var} | YES/NO | YES/NO |
{CONTRACTS} -- Move modules to analyze
{ASSET_TYPES} -- FungibleAsset metadata and Coin<T> types in scope
{ENTRY_FUNCTIONS} -- Functions where tokens enter the protocol
{EXIT_FUNCTIONS} -- Functions where tokens leave the protocol
{BALANCE_VARS} -- Internal balance tracking variables
{EXTERNAL_TOKENS} -- External token types the protocol interacts with
**ID**: [TF-N]
**Severity**: [based on fund impact]
**Step Execution**: checkmark1,2,3,4,5,6,7,8,9,10 | x(reasons) | ?(uncertain)
**Rules Applied**: [R1:Y, R4:Y, R10:Y, R11:Y]
**Location**: module::function:LineN
**Title**: [Asset type] can enter/exit via [path] without [expected accounting update]
**Description**: [Trace the token flow and where it diverges from expected]
**Impact**: [What breaks: exchange rates, user balances, protocol insolvency]
| Field | Required | Description | |-------|----------|-------------| | asset_inventory | yes | All asset types and their models | | entry_points | yes | Token entry paths with accounting status | | exit_points | yes | Token exit paths with accounting status | | balance_tracking | yes | Internal vs on-chain balance analysis | | unsolicited_vectors | yes | Unsolicited deposit analysis (R11 5 dimensions) | | type_confusion | yes | Token type validation issues | | finding | yes | CONFIRMED / REFUTED / CONTESTED | | evidence | yes | Code locations with line numbers | | step_execution | yes | Status for each step |
| Section | Required | Completed? | Notes | |---------|----------|------------|-------| | 1. Asset Inventory | YES | Y/x/? | | | 2. Token Entry Points | YES | Y/x/? | | | 3. Token Exit Points | YES | Y/x/? | | | 4. Balance Tracking Analysis | YES | Y/x/? | | | 5. Unsolicited Deposit Analysis | YES | Y/x/? | | | 5b. Unsolicited Transfer Matrix (All Types) | YES | Y/x/? | MANDATORY -- never skip (R11) | | 6. Token Type Confusion | YES | Y/x/? | | | 7. Dispatchable Hook Impact | IF dispatchable tokens | Y/x(N/A)/? | | | 8. Zero-Value Operations | YES | Y/x/? | | | 9. Cross-Token Interactions | IF multi-token | Y/x(N/A)/? | | | 10. Token Flow Checklist | YES | Y/x/? | |
After Section 5 (Unsolicited Deposit Analysis):
After Section 6 (Token Type Confusion):
ZERO_STATE_RETURN.md for first-depositor amplification via unsolicited depositsAfter Section 7 (Dispatchable Hook Impact):
development
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline