RustFund

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

Contrubitor's Contribution amount is not updated

Summary

The contribute function in the contract does not correctly update the contributor’s recorded contribution amount. This could lead to inconsistencies in tracking individual contributions.

Vulnerability Details

In the contribute function, when a user contributes to the fund, the amount they contribute is added to fund.amount_raised, but their individual contribution record (contribution.amount) is not updated. This means that while the total funds raised will be correct, individual contributors will not have an accurate record of their contributions in the system. As a result contributors will not be able to get any refund in case goal is not reached, losing all tokens

Impact

  • Contributors will not have their contributions correctly recorded, which will affect refunds

Tools Used

  • Manual code review

PoC

Comment out the whole rustfund.ts file and create a new file in tests/ named PoC.ts and paste the following code:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Rustfund } from "../target/types/rustfund";
import { PublicKey } from '@solana/web3.js';
import { assert, expect } from 'chai';
describe("PoC", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Rustfund as Program<Rustfund>;
const creator = provider.wallet;
const otherUser = anchor.web3.Keypair.generate();
const otherUser2 = anchor.web3.Keypair.generate();
const otherUser3 = anchor.web3.Keypair.generate();
const fundName = "firstflight Fund";
const description = "this program is for firstflight";
const goal = new anchor.BN(1000000000); // 1 SOL
const contribution = new anchor.BN(500000000); //0.5 SOL
const deadline = new anchor.BN(Math.floor(Date.now() / 1000) + 10); // 10 sec from now (testing)
let fundPDA: PublicKey;
let fundBump: number;
let contributionPDA: PublicKey;
let contributionBump: number;
before(async () => {
// Generate PDA for fund
[fundPDA, fundBump] = await PublicKey.findProgramAddress(
[Buffer.from(fundName), creator.publicKey.toBuffer()],
program.programId
);
});
it("Creates a fund", async () => {
await program.methods
.fundCreate(fundName, description, goal)
.accounts({
fund: fundPDA,
creator: creator.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
console.log("fundName", fund.name);
console.log("fundDescription", fund.description);
console.log("fundGoal", fund.goal);
console.log("fundCreator", fund.creator);
console.log("fundAmountRaised", fund.amountRaised);
});
it("Sets a deadline", async () => {
await program.methods
.setDeadline(deadline)
.accounts({
fund: fundPDA,
creator: creator.publicKey,
})
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
console.log("fundDeadline", fund.deadline);
});
it("Contributes to fund", async () => {
// Generate PDA for contribution
[contributionPDA, contributionBump] = await PublicKey.findProgramAddress(
[fundPDA.toBuffer(), provider.wallet.publicKey.toBuffer()],
program.programId
);
await program.methods
.contribute(contribution)
.accounts({
fund: fundPDA,
contributor: provider.wallet.publicKey,
contribution: contributionPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
const contributionAccount = await program.account.contribution.fetch(contributionPDA);
console.log("fundBalanceAfter", await provider.connection.getBalance(fundPDA));
assert(contributionAccount.amount.toNumber()==0) // Contribution amount not updated
});
})

Recommendations

  • Update the contributor’s recorded contribution amount in the contribute function:

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
if fund.deadline != 0 && fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineReached.into());
}
// Initialize or update contribution record
if contribution.contributor == Pubkey::default() {
contribution.contributor = ctx.accounts.contributor.key();
contribution.fund = fund.key();
contribution.amount = 0;
}
// Transfer SOL from contributor to fund account
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.contributor.to_account_info(),
to: fund.to_account_info(),
},
);
system_program::transfer(cpi_context, amount)?;
fund.amount_raised += amount; // @audit - user's contribution amount is not being updated
+ contribution.amount += amount;
Ok(())
}
Updates

Appeal created

bube Lead Judge 5 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.