Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Severity: low
Valid

Fund name impersonation, unreclaimed rent on closed/completed funds, and dealine_set typo

Root + Impact

Description

programs/rustfund/src/lib.rs exhibits three design and code-hygiene issues across fund_create, withdraw, and the Fund account struct.

First, fund_create derives the Fund PDA from [name.as_bytes(), creator.key().as_ref()] without enforcing global uniqueness on name. Because two different creators produce two distinct PDAs for the exact same campaign name, a malicious actor can create an impersonating fund with an identical name to mislead contributors into donating to the wrong campaign.

Second, neither Fund nor Contribution accounts are ever closed after withdraw or refund. When withdraw transfers the raised lamports out of the Fund PDA, the 8 + Fund::INIT_SPACE bytes and rent-exempt lamports remain permanently allocated on-chain, and every Contribution account remains open indefinitely, resulting in an unrecoverable rent leak for creators and contributors.

Third, the boolean field dealine_set on the Fund struct is misspelled, which is cosmetic but reflects that the deadline-tracking flag was not thoroughly exercised.

#[account]
#[derive(InitSpace)]
pub struct Fund {
pub creator: Pubkey,
#[max_len(32)]
pub name: String,
pub goal: u64,
pub amount_raised: u64,
pub deadline: u64,
@> pub dealine_set: bool,
}

Risk

Likelihood: Low — Requires user confusion over identically named campaigns or repeated campaign creation without account closure.

Impact: Low — Does not directly steal funds from an existing Fund PDA, but enables campaign impersonation and permanently locks rent-exempt lamports in Fund and Contribution accounts.

Proof of Concept

Because Fund seeds include creator.key(), any second user can call fund_create with the same name as an existing campaign. Additionally, after withdraw completes, no close constraint or instruction exists to reclaim rent from Fund or Contribution accounts:

// 1. Legitimate creator initializes "Charity Fund" -> PDA(["Charity Fund", creator_a])
// 2. Attacker initializes "Charity Fund" -> PDA(["Charity Fund", attacker])
// Both succeed because the seeds differ by creator pubkey.
// 3. After withdraw(), the Fund account's lamports are only reduced by amount_raised,
// leaving rent-exempt lamports permanently locked in the PDA.

Recommended Mitigation

Enforce or clearly surface the creator's public key alongside the fund name in client interfaces, add a close = creator (or dedicated close instruction) once a campaign is finalized so rent can be reclaimed, and rename dealine_set to deadline_set.

#[account]
#[derive(InitSpace)]
pub struct Fund {
pub creator: Pubkey,
#[max_len(32)]
pub name: String,
pub goal: u64,
pub amount_raised: u64,
pub deadline: u64,
- pub dealine_set: bool,
+ pub deadline_set: bool,
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-04] Unclaimed rent from fund and contribution accounts leads to permanent SOL lockup

## Description neither the `withdraw()` nor `refund()` functions properly close their respective accounts after the funds have been transferred. This results in the rent amount (the SOL required to keep the account allocated on the Solana blockchain) remaining locked in these accounts indefinitely. ## Vulnerability Details In Solana, accounts must maintain a minimum balance to remain "rent-exempt", ensuring they aren't purged from the blockchain. When accounts are no longer needed, best practice is to close them and return this rent to the appropriate party (typically the account creator). The RustFund protocol currently lacks this account cleanup mechanism. - The withdrawal process in `withdraw()` transfers the raised funds from the fund account to the creator - Similarly, the refund process in `refund()` transfers the contribution amount back to the contributor and resets the contribution amount In both functions, the account data is updated, but the accounts themselves remain open, with their rent amount locked. After all funds have been withdrawn or refunded, these accounts serve no further purpose but continue to consume blockchain resources and lock up SOL. ### Proof of Concept 1. Alice creates a fund with a goal of 100 SOL, which requires 0.1 SOL as rent for the fund account. 2. The fund successfully raises 100 SOL from various contributors, each of whom also paid rent for their contribution accounts (approximately 0.05 SOL each). 3. Alice calls `withdraw()` to claim the 100 SOL raised funds, but the fund account itself is not closed. 4. The 0.1 SOL rent for the fund account remains locked in the account indefinitely. 5. Similarly, when contributors request refunds through the `refund()` function, their contribution accounts are updated but not closed. 6. The rent for all contribution accounts (e.g., 10 contributors × 0.05 SOL = 0.5 SOL) remains locked indefinitely. 7. Over time, as more funds are created and more contributions are made, the amount of permanently locked SOL grows. ## Impact - Economic Inefficiency, - Blockchain bloat, - Reduced user returns ## Recommendations Implement proper account closure in both the `withdraw()` and `refund()` functions.

Support

FAQs

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

Give us feedback!