agents/skills/aptos/zero-state-return/SKILL.md
Trigger Pattern Vault/pool/first-depositor pattern detected - Inject Into Depth-edge-case
npx skillsauth add plamentsv/plamen zero-state-returnInstall 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: Vault/pool/first-depositor pattern detected Inject Into: Depth-edge-case Purpose: Analyze zero-state transitions in Aptos Move protocols -- initial zero state, return to zero after operations, residual assets, and re-entry vulnerabilities
This skill covers BOTH initial zero state AND return-to-zero-state analysis:
Find all vault/pool/staking mechanisms and their zero-state boundaries:
| State | Resource / Variable | Zero Condition | Trigger | Code Location |
|-------|-------------------|----------------|---------|---------------|
| Total shares | {resource.total_supply} | == 0 | All users withdrew/burned | {module:line} |
| Total assets | {resource.total_assets} | == 0 | No funds deposited | {module:line} |
| Pool liquidity | {resource.reserves} | Both reserves == 0 | All LP withdrawn | {module:line} |
| Staking pool | {resource.total_staked} | == 0 | All unstaked | {module:line} |
For each state: what is the protocol behavior when this condition is true?
Can the first depositor manipulate share price?
| Protocol | Formula | When totalShares == 0 | First Deposit Behavior |
|----------|---------|----------------------|----------------------|
| {name} | shares = amount * totalShares / totalAssets | {special case?} | {describe} |
Classic first depositor attack on Aptos:
totalAssets increases but totalShares stays at 1Checks:
totalAssets?| Protection | Present? | Implementation | Bypass Possible? | |-----------|----------|----------------|-----------------| | Minimum first deposit | YES/NO | {code ref} | {analysis} | | Virtual shares/assets offset | YES/NO | {code ref} | {analysis} | | Dead shares (initial mint to zero) | YES/NO | {code ref} | {analysis} | | Internal accounting (not balance-based) | YES/NO | {code ref} | {analysis} | | Decimal offset in share calculation | YES/NO | {code ref} | {analysis} |
After normal operations, can the protocol return to zero state?
| Scenario | Trigger | Residual State After | Re-entry Safe? | |----------|---------|---------------------|---------------| | All shares redeemed | Last user withdraws | {what remains?} | YES/NO | | Emergency withdraw | Admin drains | {what remains?} | YES/NO | | All stakers unstake | Last unstake | {what remains?} | YES/NO | | Pool fully drained | All LP removed | {what remains?} | YES/NO |
Trace the withdrawal/burn path:
totalShares never reaches 0 -- is this protection consistent?When supply returns to zero, check for stranded value:
| Reward Source | Persists When totalShares = 0? | Claimable By Next Depositor? | Amount Bounded? | |-------------|-------------------------------|-----------------------------:|----------------| | {reward_source} | YES/NO | YES/NO | {max amount or UNBOUNDED} |
If rewards persist AND next depositor can claim -> FINDING (severity based on amount).
| Fee Type | Persists When totalShares = 0? | Captured By Next Depositor? | Reconciliation Mechanism? | |----------|-------------------------------|----------------------------|--------------------------| | {fee_type} | YES/NO | YES/NO | {mechanism or NONE} |
totalAssets = 1 wei, totalShares = 0)totalAssets > 0 AND totalShares == 0 explicitly?Does re-entering zero state recreate first-depositor attack conditions?
| Scenario | Initial State | Return-to-Zero State | Same Vulnerability? | |----------|---------------|---------------------|---------------------| | First depositor attack | totalSupply=0, totalAssets=0 | totalSupply=0, totalAssets=X (residual) | WORSE if residual > 0 | | Exchange rate manipulation | No shares exist | No shares, but balance exists | YES + amplified | | Donation attack | Clean state | Dirty state | YES + pre-seeded |
Key question: Is the first-deposit protection (from Section 2b) applied ONLY on initial deployment, or does it also trigger when totalShares returns to 0?
Trace the share minting code:
// Pattern: Protection covers initial AND return-to-zero
if (total_shares == 0) {
// First deposit logic with protection
}
// vs Pattern: Protection only on first-ever deposit
if (!initialized) {
// Protection here
} else if (total_shares == 0) {
// NO protection -- vulnerable on return-to-zero
}
For each state field used in arithmetic or control flow, check its initial value before any user interaction:
@0x0 for addresses). If a function uses last_timestamp, start_time, or last_update in subtraction or division BEFORE it has ever been set, the result may be unexpected (e.g., timestamp::now_seconds() - 0 = enormous elapsed time, or division by a value derived from 0).| Expression | When totalShares = 0 | Behavior | Impact |
|-----------|---------------------|----------|--------|
| amount * totalShares / totalAssets | 0 / totalAssets | Returns 0 | {impact} |
| amount * totalAssets / totalShares | amount * X / 0 | ABORT | {DoS, broken withdrawal} |
| rewards / totalShares | rewards / 0 | ABORT | {reward distribution broken} |
For each division: is there a zero-check guard? If not, what transaction aborts?
| Operation | At Zero State | Result | Expected? | |-----------|--------------|--------|-----------| | deposit(0) at totalShares=0 | {behavior} | {shares issued?} | {analysis} | | withdraw(0) at totalShares=0 | {behavior} | {aborts?} | {analysis} | | claim_rewards() at totalShares=0 | {behavior} | {rewards distributed?} | {analysis} |
Check for admin functions that can force zero state:
| Function | Access Control | Clears All State? | Residual After Reset | |----------|---------------|-------------------|---------------------| | {emergency_withdraw_fn} | {who} | YES/NO | {what remains} | | {rescue_tokens_fn} | {who} | YES/NO | {what remains} | | {pause + drain_fn} | {who} | YES/NO | {what remains} | | {migrate_fn} | {who} | YES/NO | {what remains in old module} |
For each: what state persists after the "reset"? Can it be exploited?
{CONTRACTS} -- Move modules containing vault/pool logic
{SHARE_VARIABLES} -- Variables tracking total shares/supply
{ASSET_VARIABLES} -- Variables tracking total assets/deposits
{SHARE_MINT_FORMULA} -- Share calculation formula at deposit
{FIRST_DEPOSIT_GUARDS} -- Existing first-deposit protections
**ID**: [ZS-N]
**Severity**: [typically HIGH if funds extractable, MEDIUM if DoS]
**Step Execution**: checkmark1,2,3,4,5,6,7 | x(reasons) | ?(uncertain)
**Rules Applied**: [R4:Y, R10:Y, R11:Y]
**Location**: module::function:LineN
**Title**: [Zero-state type] allows [attack] due to [residual state / missing protection]
**Description**:
- Protocol can reach totalShares=0 via [mechanism]
- When this happens, [state variable] retains value of [amount]
- A new depositor can [exploit path]
**Impact**: [Fund extraction / exchange rate manipulation / DoS]
| Field | Required | Description | |-------|----------|-------------| | zero_state_transitions | yes | All paths to zero state | | first_depositor_analysis | yes | First deposit attack assessment | | residual_assets | yes | What persists after zero state | | re_entry_vulnerability | yes | Whether return-to-zero recreates first-depositor conditions | | edge_cases | yes | Division by zero and zero-amount operations | | finding | yes | CONFIRMED / REFUTED / CONTESTED | | evidence | yes | Code locations with line numbers | | step_execution | yes | Status for each step |
| Section | Required | Completed? | Notes | |---------|----------|------------|-------| | 1. Identify Zero-State Transitions | YES | Y/x/? | | | 2. First Depositor Analysis | YES | Y/x/? | Including 2a formula + 2b protections | | 3. Return to Zero Analysis | YES | Y/x/? | Including 3a scenarios + 3b exact zero trace | | 4. Residual Asset Check | YES | Y/x/? | All sub-checks: 4a rewards, 4b fees, 4c dust, 4d pending | | 5. Re-Entry Vulnerability Analysis | YES | Y/x/? | Compare initial vs return-to-zero protections | | 6. Empty Pool Edge Cases | YES | Y/x/? | Division by zero + zero-amount ops | | 7. Protocol Reset Functions | IF admin reset exists | Y/x(N/A)/? | |
After Section 2 (First Depositor): Cross-reference with TOKEN_FLOW_TRACING.md Section 5 for unsolicited deposit vectors that amplify first-depositor attacks.
After Section 4 (Residual Assets): If residual rewards/fees found, cross-reference with ECONOMIC_DESIGN_AUDIT.md for whether fee/reward accumulation is bounded.
After Section 5 (Re-Entry): If return-to-zero is possible AND first-deposit protection is initial-only -> FINDING (minimum Medium, upgrade to High if unsolicited deposits can amplify).
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