[L-1] Lack of Refund Event Emission (Omission + Transparency Issue)
Description:
The original refund function does not emit an event to log refund transactions. This omission means there’s no on-chain notification of refund activity, forcing external systems (e.g., indexers, user interfaces) to rely solely on scanning account state changes to detect refunds.
Impact:
The absence of an event reduces transparency and complicates off-chain monitoring, debugging, and auditing. Users and external tools cannot easily track refund activity without additional effort, potentially undermining trust in the program and increasing operational overhead. While it doesn’t directly affect on-chain functionality or lead to fund loss, it’s a significant usability and design flaw for a crowdfunding program like rustfund
.
Proof of Concept:
pub fn refund(ctx: Context<FundRefund>) -> Result<()> {
let amount = ctx.accounts.contribution.amount;
if ctx.accounts.fund.deadline != 0 && ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineNotReached.into());
}
**ctx.accounts.fund.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.fund.to_account_info().lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
**ctx.accounts.contributor.to_account_info().try_borrow_mut_lamports()? =
ctx.accounts.contributor.to_account_info().lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
ctx.accounts.contribution.amount = 0;
Ok(())
}
Recommended Mitigation:
Add an event emission to log refund details:
pub fn refund(ctx: Context<FundRefund>) -> Result<()> {
let fund = &mut ctx.accounts.fund;
let contribution = &mut ctx.accounts.contribution;
let contributor = &mut ctx.accounts.contributor;
if fund.deadline != 0 && (fund.deadline as i64) > Clock::get()?.unix_timestamp {
return Err(ErrorCode::DeadlineNotReached.into());
}
let amount = contribution.amount;
contribution.amount = 0;
**fund.to_account_info().try_borrow_mut_lamports()? =
fund.to_account_info().lamports()
.checked_sub(amount)
.ok_or(ProgramError::InsufficientFunds)?;
**contributor.to_account_info().try_borrow_mut_lamports()? =
contributor.to_account_info().lamports()
.checked_add(amount)
.ok_or(ErrorCode::CalculationOverflow)?;
+ // Emit event for transparency
+ emit!(RefundEvent {
+ fund: fund.key(),
+ contributor: contributor.key(),
+ amount,
+ });
Ok(())
}
+#[event]
+pub struct RefundEvent {
+ pub fund: Pubkey,
+ pub contributor: Pubkey,
+ pub amount: u64,
+}