agents/skills/aptos/type-safety/SKILL.md
Trigger Pattern Always (Aptos Move) - generic type exploitation - Inject Into Breadth agents, depth-state-trace
npx skillsauth add plamentsv/plamen type-safetyInstall 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: Always (Aptos Move) --- generic type exploitation Inject Into: Breadth agents, depth-state-trace
Move's type system is its primary security mechanism. Generic type parameters allow modules to be polymorphic, but incorrect or insufficient type constraints enable attackers to substitute unexpected types, bypass access control, confuse token types, or exploit phantom type assumptions. This skill audits every generic interface for type safety violations.
STEP PRIORITY: Steps 2 (Type Parameter Substitution) and 5 (Coin/FungibleAsset Type Confusion) are where HIGH/CRITICAL severity findings most commonly hide. Do NOT rush these steps. If constrained, skip conditional sections (3, 4) before skipping 2 or 5.
Enumerate ALL public, public(friend), and entry functions with generic type parameters:
| Function | Module | Type Params | Constraints | Visibility | Entry? | Who Can Call |
|----------|--------|-------------|-------------|-----------|--------|-------------|
| withdraw<T> | vault | T | key | public | YES | Any signer |
| swap<X, Y> | dex | X, Y | store | public | YES | Any signer |
MANDATORY GREP: Search all .move files for fun .*< to find every generic function. Include internal (fun), public(friend) fun, public fun, and public entry fun.
For each generic function, additionally note:
store)For each generic function identified in Step 1, analyze what happens when an attacker substitutes an unexpected type:
| Function | Type Param | Expected Type | Attacker Substitutes | Guard Against Wrong Type? | Impact |
|----------|-----------|---------------|---------------------|--------------------------|--------|
| withdraw<T>(store) | T | RealCoin | FakeCoin (attacker-defined) | YES --- {mechanism} / NO | {impact} |
Attack methodology per function:
TypeInfo and checks against it)&TypeWitness<T>)assert!(exists<Pool<T>>(@protocol), E_INVALID_TYPE))coin::is_coin_initialized<T>() checkmodule attacker::fake { struct FakeCoin has store {} } and calls withdraw<FakeCoin>()?For protocols with pools, markets, or vaults parameterized by type:
| Pool/Market | Type Parameter | Can Attacker Create Pool With Arbitrary Type? | Impact If Confusion |
|-------------|---------------|----------------------------------------------|---------------------|
| Pool<T> | T | YES --- anyone can call create_pool<T>() / NO | {drain, mispricing, accounting error} |
Check: If Pool<RealCoin> and Pool<FakeCoin> exist, can operations on one affect the other? Common issues:
Tag: [TRACE:substitute T=FakeCoin → {function} → {bypass/confusion} → impact: {X}]
For structs with phantom type parameters (phantom T):
| Struct | Phantom Param | Purpose | Runtime Impact of T | Can T Be Forged? |
|--------|--------------|---------|--------------------|--------------------|
| Pool<phantom CoinType> | CoinType | Type-tag discrimination | None (phantom) | {analysis} |
Phantom type rules in Move:
Pool<USDC> vs Pool<WETH> are different types at the Move level but identical at the bytecode level.Check for each phantom type:
Pool<AttackerCoin> that interacts with Pool<USDC> state)Wrapper<phantom T> containing Inner<T> --- is T phantom in Inner too?)| Pattern | Risk | Check | |---------|------|-------| | Phantom used for access control | Medium | Can attacker define their own type to bypass access gate? | | Phantom used for pool isolation | High | Does pool isolation rely solely on phantom type discrimination? | | Phantom type in event emission | Low | Can attacker emit events with spoofed phantom types for off-chain confusion? |
For functions that accept type witnesses:
| Witness Struct | Creating Module | Who Can Create? | Functions That Accept It | Properly Gated? |
|---------------|----------------|----------------|------------------------|-----------------|
| TypeWitness<T> | {module} | {analysis} | {list} | YES/NO |
Type witness pattern is Move's equivalent of capability-based access control at the type level. A type witness is a struct that can only be created by the module that defines the associated type. Functions that require a witness parameter are restricted to callers authorized by that module.
Check for each witness:
drop? If yes, it can be created once and reused --- is this intended?copy? If yes, it can be duplicated --- does this break single-use assumptions?store? If yes, it can be persisted --- can an attacker store a witness and replay it later?For each witness used for access control:
1. TypeWitness<T> is required by function F
2. TypeWitness<T> can be created by: {list of functions/modules}
3. Can attacker reach a creation path? {YES/NO --- trace}
4. If YES: attacker creates witness and calls F with unauthorized type T
5. Impact: {unauthorized operation}
For all functions that handle Coin<T> or FungibleAsset:
| Function | Accepts | Type Restriction | Enforcement Mechanism | Bypass Possible? |
|----------|---------|-----------------|----------------------|-----------------|
| deposit<T>(coin: Coin<T>) | Coin<T> | T must be registered | assert!(is_registered<T>()) | {analysis} |
Check for each coin-handling function:
Coin<T>?Coin<FakeCoin> and withdraw Coin<RealCoin>?swap<X, Y>): are X and Y validated to be a supported pair? Can attacker swap between arbitrary types?For protocols using the Aptos Fungible Asset standard:
| Function | Metadata Check | Object Address Validated? | Impact If Wrong Metadata |
|----------|---------------|--------------------------|--------------------------|
| {function} | YES --- assert!(metadata == expected) / NO | YES/NO | {wrong asset deposited/withdrawn} |
Aptos FA-specific checks:
FungibleAsset is NOT parameterized by type --- it uses a metadata object address for discrimination. This means type-level enforcement does NOT apply. All FungibleAssets have the same Move type.FungibleStore addresses properly validated when reading balances?Tag: [TRACE:deposit FakeCoin to Pool<RealCoin> → withdraw RealCoin → drain pool]
For protocols that handle BOTH Coin<T> and FungibleAsset:
| Operation | Which Standard Used? | Consistent? | Can Attacker Force Wrong Standard? | |-----------|---------------------|-------------|-----------------------------------| | deposit | Coin<T> | --- | --- | | withdraw | FungibleAsset | MISMATCH | {analysis} |
Check: If deposit uses Coin<T> but withdraw uses FungibleAsset (or vice versa), is the accounting consistent? The Aptos framework provides conversion between Coin<T> and FungibleAsset, but the conversion path may not be symmetric or may bypass module-level accounting.
Only the module that defines a struct can create instances of it. This is Move's module encapsulation guarantee. Audit for violations:
| Struct | Defining Module | Public Functions That Return New Instances | Should Creation Be Public? | |--------|----------------|------------------------------------------|--------------------------| | {name} | {module} | {list or NONE} | YES/NO --- {reason} |
Check for each struct:
public or public(friend) function return a newly created instance of this struct?friend functions that create instances: are ALL friend modules trusted to create instances responsibly?| Module | Friend Modules | What Friends Can Create | Trust Justified? | |--------|---------------|------------------------|-----------------| | {module} | {friends list} | {structs accessible via friend functions} | {analysis} |
Check: The friend declaration grants the friend module full access to public(friend) functions, including creation functions. If a friend module has a vulnerability, it can be used to mint/create unauthorized instances.
MANDATORY: For each friend relationship, verify: if the friend module is compromised (has a vulnerability), what is the maximum damage to the declaring module? If friend can create value-bearing structs → severity minimum HIGH.
When this skill identifies an issue:
**ID**: [TS-N]
**Severity**: [based on type confusion impact --- fund loss from wrong type = Critical]
**Step Execution**: check1,2,3,4,5,6 | X(reasons) | ?(uncertain)
**Rules Applied**: [R1:Y, R4:Y, R5:Y, R10:Y]
**Depth Evidence**: [TRACE:substitute T=X → bypass → impact], [BOUNDARY:type=FakeCoin]
**Location**: module::function (source_file.move:LineN)
**Title**: [Function] accepts arbitrary type [T] without [validation], enabling [type confusion/drain/forgery]
**Description**: [Trace from attacker type substitution through function logic to impact]
**Impact**: [Fund drain, unauthorized minting, accounting corruption, pool confusion]
CRITICAL: You MUST report completion status for ALL sections. Steps 2 and 5 are highest priority.
| Section | Required | Completed? | Notes | |---------|----------|------------|-------| | 1. Generic Function Inventory | YES | Y/X/? | MANDATORY --- grep ALL .move files | | 2. Type Parameter Substitution | YES | Y/X/? | MANDATORY --- highest-severity source | | 2b. Cross-Pool Type Confusion | IF pools/markets parameterized by type | Y/X(N/A)/? | | | 3. Phantom Type Audit | IF phantom types used | Y/X(N/A)/? | | | 4. Type Witness Pattern | IF witness pattern used | Y/X(N/A)/? | | | 5. Coin/FA Type Confusion | YES | Y/X/? | MANDATORY --- fund loss vector | | 5b. FungibleAsset Metadata | IF FA standard used | Y/X(N/A)/? | Metadata validation | | 5c. Mixed Standard Confusion | IF both Coin and FA used | Y/X(N/A)/? | | | 6. Module Type Authority | YES | Y/X/? | Creation function audit | | 6b. Friend Module Trust | IF friend declarations exist | Y/X(N/A)/? | Friend compromise analysis |
After Section 2 (Type Parameter Substitution):
ABILITY_ANALYSIS.md Section 7 for ability constraint gapsAfter Section 5 (Coin/FA Type Confusion):
After Section 6 (Module Type Authority):
For Sections 2 and 5, you MUST produce output even if no issues found:
Section 2 Output (always required):
### 2. Type Parameter Substitution Analysis
| Function | Type Param | Expected Type | Enforcement | Substitution Blocked? |
|----------|-----------|---------------|-------------|----------------------|
| {function} | T | {expected} | {mechanism or NONE} | YES/NO |
**If enforcement = NONE for any value-handling function**: Finding verdict minimum PARTIAL.
Section 5 Output (always required):
### 5. Coin/FungibleAsset Type Confusion
| Function | Standard | Type Restriction | Enforcement | Bypass Possible? |
|----------|---------|-----------------|-------------|-----------------|
| {function} | Coin/FA | {restriction} | {mechanism} | YES/NO |
**If ANY coin-handling function lacks type enforcement**: Verdict CONFIRMED, severity based on fund 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