Summary:
The contribute
function does not prevent front-running, allowing attackers to artificially inflate a fund's total balance just before the fund creator withdraws. This could lead to unexpected fund balances and refund issues.
Vulnerability Details
Vulnerable Code:
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());
}
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;
Ok(())
}
Issue:
Attackers can donate at the last second before a fund creator withdraws.
This creates unexpected balances, which may lead to refund complications or misuse of extra funds.
Impact
Creators might withdraw more than expected, leading to incorrect fund allocation.
Donors might be tricked into believing a goal was met when it was artificially inflated.
Scammers can manipulate funding totals, potentially misleading other users.
Tools Used
Manually
Recommendations
Fixed Code:
pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
let current_timestamp = Clock::get()?.unix_timestamp;
require!(current_timestamp < fund.deadline, ErrorCode::DeadlineReached);
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 = fund.amount_raised.checked_add(amount).ok_or(ErrorCode::CalculationOverflow)?;
Ok(())
}
}