Manages the full lifecycle of Solana accounts including creation, initialization, resize, migration, and closing with rent-exemption validation and lamport drainage safety
curl -s https://parity.cx/api/skills/account-lifecycle | shcopyYou are analyzing how a Solana program manages the full lifecycle of its accounts: creation, initialization, mutation, reallocation, migration, and closure. Account lifecycle errors are a major source of vulnerabilities. Improper initialization allows reinitialization attacks. Improper closure leaves stale data or loses lamports. Missing rent checks cause accounts to be garbage-collected.
| Source | Path | Contains |
|--------|------|----------|
| Vulnerability Rules | programs/parity/src/context_engine.rs > VULNERABILITY_RULES | reinitialization-attack, close-account-drain, rent-exemption patterns |
| Curated Audit Findings | programs/parity/src/context_engine.rs > CURATED_AUDIT_FINDINGS | Real close-account and state management vulnerabilities |
| Framework Patterns | programs/parity/src/context_engine.rs > ANCHOR_PATTERNS | Correct account-initialization, close-account patterns |
| Security Audit Skill | skills/security-audit/SKILL.md | Reinitialization (Step 7) and close safety (Step 8) checks |
| PDA Helper Skill | skills/pda-helper/SKILL.md | PDA creation and bump storage patterns |
GitHub base URL: https://github.com/paritydotcx/Skills/blob/main/
For each #[account] type, determine its lifecycle:
Build a state diagram:
[uninitialized] --init--> [active] --update--> [active] --close--> [closed]
--resize--> [active]
--migrate-> [active_v2]If an account type has no close instruction, flag it (lamports are permanently locked).
For every init constraint:
space = 8 + T::INIT_SPACE is present and correct. Manual byte counting is fragile.payer account is a Signer and mut (will be debited for rent).init constraint handles this by checking the discriminator, but init_if_needed does not.init_if_needed review**: If used, verify the instruction handles both the "new account" and "existing account" code paths correctly. The handler must check whether the account was just created or already existed.// SAFE: init fails if account already exists
#[account(init, payer = user, space = 8 + Vault::INIT_SPACE)]
// CAUTION: init_if_needed allows re-entry into init logic
#[account(init_if_needed, payer = user, space = 8 + Vault::INIT_SPACE)]Finding if violated:
reinitialization-attackFor every account created or resized:
init handles this automatically)create_account, verify space and lamport calculations include the rent-exempt minimumrealloc, verify the account still meets rent-exemption threshold// Manual rent calculation (if not using Anchor init)
let rent = Rent::get()?;
let required_lamports = rent.minimum_balance(account_space);Finding if violated:
rent-exemptionIf the program uses realloc:
#[account(
mut,
realloc = 8 + new_size,
realloc::payer = authority,
realloc::zero = true, // zero-init new bytes
)]Finding if violated:
For every close operation:
**Anchor close constraint:**
#[account(mut, close = destination, has_one = authority)]
pub target: Account<'info, SomeAccount>,Anchor handles: lamport drain to destination, data zeroing, discriminator clearing.
Manual close (if not using Anchor constraint):
Verify all three steps are performed:
// VULNERABLE: data not zeroed
let dest = ctx.accounts.destination.to_account_info();
let target = ctx.accounts.target.to_account_info();
**dest.try_borrow_mut_lamports()? += target.lamports();
**target.try_borrow_mut_lamports()? = 0;
// Missing: target.data.borrow_mut().fill(0);Checks:
Finding if violated:
close-account-drainIf the program supports account data migration (version upgrades):
Finding if applicable:
Check for account types that:
Finding if applicable:
| Severity | Penalty |
|----------|---------|
| Critical | -25 |
| High | -15 |
| Medium | -8 |
| Info | -3 |
{
"score": 72,
"lifecycle_map": {
"Vault": {
"init": "initialize",
"mutations": ["deposit", "withdraw"],
"realloc": null,
"close": "close_vault",
"migration": null,
"rent_safe": true,
"close_safe": true,
"reinit_safe": true
},
"Config": {
"init": "initialize_config",
"mutations": ["update_config"],
"realloc": null,
"close": null,
"migration": null,
"rent_safe": true,
"close_safe": null,
"reinit_safe": true,
"issues": ["no close instruction, lamports locked"]
}
},
"findings": [...],
"summary": "3 account types analyzed. 1 high (missing data zero on close), 1 info (Config has no close instruction). Init safety: all accounts protected against reinitialization."
}