RustFund

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

Untracked Contributions Break Refund Logic

Summary

The contribute function fails to track individual contributions, which prevents contributors from claiming refunds if the funding goal is not met. While the amount_raised is updated, contributors’ specific deposits are not recorded, making it impossible to process refunds accurately.

Vulnerability Details

  • Missing Contribution Update: The function initializes contribution.amount but does not increment it when a contributor donates multiple times.

  • Refunds Depend on Contribution Tracking: Since refunds are based on individual contributions, failing to track them renders refunds impossible or inaccurate.

  • Potential Fund Mismanagement: Users may not be able to reclaim their rightful funds if the campaign fails to meet its goal.

Impact

  • Broken Refund Logic: if the campaign fails, contributors cannot get their money back, since their exact contributions are lost.

  • Risk of Fund Mismanagement: the system cannot correctly determine how much each contributor should be refunded.

  • Legal & Trust Issues: crowdfunding platforms rely on trust; incorrect refund behavior could lead to legal liability or user distrust.

Proof of Concept

This test case demonstrates that a contributor's multiple contributions are not tracked correctly, leading to incorrect refund amounts.

it("Breaks refund logic due to untracked contributions", async () => {
// First contribution
await program.methods
.contribute(new anchor.BN(100))
.accounts({
fund: fundPDA,
contributor: contributor.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([contributor])
.rpc();
let contribution = await program.account.contribution.fetch(contributionPDA);
console.log("First Contribution Amount:", contribution.amount.toString());
expect(contribution.amount.toString()).toBe("100");
// Second contribution (should be cumulative)
await program.methods
.contribute(new anchor.BN(50))
.accounts({
fund: fundPDA,
contributor: contributor.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([contributor])
.rpc();
contribution = await program.account.contribution.fetch(contributionPDA);
console.log("Updated Contribution Amount:", contribution.amount.toString());
// Expected behavior: Contribution should be 150 (100 + 50)
// Actual behavior: Contribution remains 100, breaking refund logic
expect(contribution.amount.toString()).toBe("150");
});

Recommendations

Proper Contribution Tracking for Refunds

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
// Track contributor's total contribution for accurate refunds
contribution.amount = contribution.amount.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
// Rest of function logic...
}
Updates

Appeal created

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

Contribution amount is not updated

Support

FAQs

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