RustFund

First Flight #36
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: low
Valid

No Account Closure in RustFund Contract

Summary

The lib.rs contract fails to properly close accounts after their lifecycle completion, specifically contribution accounts after refunds and fund accounts after withdrawals. This leads to persistent storage allocation on the Solana blockchain, incurring unnecessary rent costs and cluttering the blockchain state.

The contract exhibits two primary state management issues:

  1. Contribution Account Persistence:

  • Location: refund function

  • Issue: After processing a refund, the contribution account is reset to 0 amount but not closed

ctx.accounts.contribution.amount = 0; // Account remains open
// Missing: account closure
  1. Fund Account Persistence:

  • Location: withdraw function

  • Issue: After creator withdraws funds, the fund account remains allocated with zero balance

// Successful withdrawal but no closure
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? = ...;
// Missing: fund account cleanup

The contract uses Anchor's account management but doesn't implement proper cleanup procedures, violating Solana's best practices for account lifecycle management.

Vulnerability Details

Impact

Blockchain State Impact:

  • Persistent dead accounts clutter the blockchain

  • Increased storage requirements for validators

  • Potential for state bloat over time

  • Unnecessary rent costs for users maintaining unused accounts

Tools Used

Manual code review

Recommendations

Implement Account Closure in refund:

pub fn refund(ctx: Context<FundRefund>) -> Result<()> {
let amount = ctx.accounts.contribution.amount;
require!(amount > 0, ErrorCode::NoContribution);
// Process refund
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
**ctx.accounts.contributor.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.contributor.lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// Close contribution account
ctx.accounts.contribution.close(ctx.accounts.contributor.to_account_info())?;
Ok(())
}

Add Fund Closure in withdraw:

pub fn withdraw(ctx: Context<FundWithdraw>) -> Result<()> {
let amount = ctx.accounts.fund.amount_raised;
require!(amount > 0, ErrorCode::NoFunds);
// Process withdrawal
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
**ctx.accounts.creator.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.creator.lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// Close fund account
ctx.accounts.fund.close(ctx.accounts.creator.to_account_info())?;
Ok(())
}
Updates

Appeal created

bube Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Accounts are not closed after withdraw and refund

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.