agents/skills/injectable/vault-accounting/SKILL.md
Protocol Type Trigger vault (detected in recon TASK 0 Step 1) - Inject Into Core state agent OR economic design agent (merge via M4 hierarchy)
npx skillsauth add plamentsv/plamen vault-accountingInstall 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.
Protocol Type Trigger:
vault(detected in recon TASK 0 Step 1) Inject Into: Core state agent OR economic design agent (merge via M4 hierarchy) Language: Both EVM and Solana (language-agnostic methodology, language-specific examples omitted) Finding prefix:[VA-N]
When decomposing this skill into depth agent investigation questions, map sections to domains:
Recon classifies protocol as vault type based on indicators: deposit/withdraw/shares/strategy/vault.
This skill adds vault-specific accounting checks that the general ECONOMIC_DESIGN_AUDIT does not cover.
For vault protocols with share-based accounting (LP tokens, vault shares):
Compute share price = total_assets / total_shares at these states: | State | total_assets | total_shares | Share Price | Expected | Issue? | |-------|-------------|-------------|-------------|----------|--------| | After deposit | {val} | {val} | {computed} | {expected} | | | After withdrawal | {val} | {val} | {computed} | {expected} | | | After profit report | {val} | {val} | {computed} | {expected} | | | After loss report | {val} | {val} | {computed} | {expected} | | | After fee harvest | {val} | {val} | {computed} | {expected} | | | After time-decay expiry | {val} | {val} | {computed} | {expected} | |
Tag: [TRACE:state={event} -> share_price={value} -> {expected_or_unexpected}]
For vaults with time-decay mechanisms (locked profit, vesting schedules, streaming distributions):
Tag: [TRACE:{transition} -> {base_var} changed -> {decay_var} unchanged -> {decay_var} > {base_var} -> {consumer} computes {wrong_result}]
For time-weighted calculations (fees, vesting, rewards) that use a (value × timeDelta) formula:
lastDistributionTime (vesting vaults), periodFinish/lastUpdateTime (Synthetix-style staking), lastFeeCollection (management fee), lastStreamUpdate (streaming))distributeYield/updateSharePrice (vesting vaults), notifyRewardAmount (Synthetix), reportProfit/report (Yearn-style), startNewEpoch (epoch-based))block.timestamp (or now) UNCONDITIONALLY, or only inside a conditional block?timeDelta in the next calculation will include time from BEFORE the new period, causing accelerated vesting/fee accrualTag: [TRACE:new_period_start → anchor_timestamp NOT updated → timeDelta includes {stale_duration} → {acceleration_factor}x acceleration]
For any accounting getter intentionally OVERRIDDEN to diverge from the real held balance (excluding an unvested/streaming/pending component):
totalSupply is zero or dust while that divergence is closing, compute shares = deposit * totalSupply / reportedAssets() with real constants for a minimal deposit — does round-down zero/critically dilute the depositor because reported-assets rose ahead of supply?Tag: [TRACE:reportedAssets() rises via vesting_elapsed → totalSupply=dust → shares=deposit*totalSupply/reportedAssets() rounds down → depositor loses principal]
For vaults where one fee type changes a ratio that another fee type reads:
For each fee accumulator (platform fees, performance fees, management fees, yield fees):
claimFees() transfer vault assets out, or mint new tokens to the fee recipient?For each fee accumulator identified in section 5:
lastRecordedSupply × sharePrice × timeDelta) and the snapshot is NOT reduced by loss events, fees accumulate on phantom assetsmin(currentSupply, lastUpdateSupply) or equivalent conservative bounding? If YES, verify the lastUpdateSupply is updated on ALL paths (deposit, withdrawal, loss, yield)totalAssets, sharePrice, exchangeRate) can be INFLATED by a preceding operation in the same transaction or administrative sequence. Pattern: admin resets a high-water mark or benchmark → fee base recalculates from a lower reference → next yield event charges fees on phantom gains that were previously below the benchmark. Check: does the fee base track CUMULATIVE gain or INCREMENTAL gain since last collection? If cumulative: can any reset/recalibration cause double-counting?For each fee claim path identified in section 5:
Tag: [TRACE:loss_event → fee_base_unchanged → feesOwed > available_assets → {impact}]
| Section | Required | Completed? | Notes | |---------|----------|------------|-------| | 1. Share Price Under Adversity | YES | | | | 2. Time-Decay State Consistency | IF time-decay mechanism detected | | | | 2b. Time-Weighted Anchor Validation | IF time-weighted mechanism detected | | | | 2c. Reported-vs-Held Asset Divergence at Dust Supply | IF overridden accounting getter detected | | | | 3. Cross-Fee Ratio Dependency | IF multiple fee types detected | | | | 4. First Depositor / Re-entry | YES | | | | 5. Fee Source Analysis | IF fee mechanism detected | | | | 5b. Fee Solvency Under Stress | IF fee mechanism detected | | | | 6. Withdrawal Fairness | IF multi-step withdrawal | | |
data-ai
Trigger Pattern Always (run during recon TASK 0, not breadth) - Inject Into Recon agent only (meta_buffer.md enrichment)
data-ai
Trigger Pattern Always (run during recon TASK 0, not breadth) - Inject Into Recon agent only (meta_buffer.md enrichment)
data-ai
Trigger Pattern Always (run during recon TASK 0, not breadth) - Inject Into Recon agent only (meta_buffer.md enrichment)
data-ai
Trigger STABLESWAP_FORK flag (fork-ancestry detects Curve/StableSwap parent via get_d/get_y/ramp_a/StableSwap patterns) - Agent Type general-purpose (standalone niche agent, 1 budget slot)