RustFund

First Flight #36
Beginner FriendlyRust
100 EXP
View results
Submission Details
Severity: low
Invalid

Missing Contribution Amount Validation

Summary

The smart contract contains a Low severity vulnerability where it accepts contributions of 0 SOL without validation, creating unnecessary state bloat and potentially confusing account state

Vulnerability Details

In the contribute() function, there is no validation to ensure that the contribution amount is greater than zero:

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
// No check that 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;
Ok(())
}

This allows users to create contribution accounts and increment the contribution counter without actually contributing any SOL to the fund.

Impact

This vulnerability has low severity because:

  • It doesn't directly result in loss of funds

  • It leads to unnecessary state bloat and rent costs

  • It creates confusing accounting where users appear to have contributed but didn't actually provide funds

POC

it("Allows contributions of 0 amount", async () => {
// Create a new fund for this test
const zeroContribFundName = "Zero Contribution Fund";
const [zeroContribFundPDA] = await PublicKey.findProgramAddress(
[Buffer.from(zeroContribFundName), creator.publicKey.toBuffer()],
program.programId
);
await program.methods
.fundCreate(zeroContribFundName, description, goal)
.accounts({
fund: zeroContribFundPDA,
creator: creator.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// Set a deadline
const contribDeadline = new anchor.BN(Math.floor(Date.now() / 1000) + 100);
await program.methods
.setDeadline(contribDeadline)
.accounts({
fund: zeroContribFundPDA,
creator: creator.publicKey,
})
.rpc();
// Generate PDA for contribution
const [zeroContribPDA] = await PublicKey.findProgramAddress(
[zeroContribFundPDA.toBuffer(), provider.wallet.publicKey.toBuffer()],
program.programId
);
// Get balance before to verify no SOL is transferred
const balanceBefore = await provider.connection.getBalance(
provider.wallet.publicKey
);
// Contribute 0 SOL
const zeroAmount = new anchor.BN(0);
await program.methods
.contribute(zeroAmount)
.accounts({
fund: zeroContribFundPDA,
contributor: provider.wallet.publicKey,
contribution: zeroContribPDA,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
const balanceAfter = await provider.connection.getBalance(
provider.wallet.publicKey
);
const fund = await program.account.fund.fetch(zeroContribFundPDA);
// Verify a new account was created but no value was transferred
const contributionAccount = await program.account.contribution.fetch(
zeroContribPDA
);
reportBug(
"LOW",
"Missing Contribution Amount Validation",
"The contract allows contributions of 0 SOL, which creates unnecessary accounts and state bloat",
`Balance before: ${balanceBefore}, after: ${balanceAfter}\n` +
`Successfully contributed 0 lamports to the fund.\n` +
`Fund amount raised: ${fund.amountRaised.toString()}\n` +
`Created contribution account with PDA: ${zeroContribPDA}\n` +
`This creates unnecessary accounts and state bloat for no actual contribution.`
);
});

Test Output:

========================================
🐛 BUG REPORT [LOW]: Missing Contribution Amount Validation
----------------------------------------
Description: The contract allows contributions of 0 SOL, which creates unnecessary accounts and state bloat
Evidence: Balance before: 499995000, after: 499994000
Successfully contributed 0 lamports to the fund.
Fund amount raised: 0
Created contribution account with PDA: AeLGbKviBFJqFKSbEUmvFQeQRKJiNcZ7uQikXYHQKbHK
This creates unnecessary accounts and state bloat for no actual contribution.
========================================

Tools Used

  • Manual code review

Recommendations

Add validation to ensure that contribution amounts are greater than zero:

pub fn contribute(ctx: Context<FundContribute>, amount: u64) -> Result<()> {
// Add this validation
require!(amount > 0, ErrorCode::ZeroContribution);
// ... rest of function
}

And add a new error code:

#[error_code]
pub enum ErrorCode {
// Existing errors...
#[msg("Cannot contribute zero amount")]
ZeroContribution,
}
Updates

Appeal created

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

[Invalid] Lack of minimal `amount` in `contribute` function

If user contributes 0 SOL, the `contribution.amount` will be updated with 0 value. There is no impact on the protocol. Also, the new contributers should pay for account creation, therefore there is no incentive someone to create a very huge number of accounts to contribute zero amount.

Support

FAQs

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