When initializing a new crowdfunding campaign via the fund_create instruction, the program accepts a goal parameter of type u64. Critically, the program fails to validate that this goal is strictly positive (> 0).
A funding goal of 0 is fundamentally contradictory to the concept of a crowdfunding campaign. If the goal is zero:
Logic Breakdown: The state of "Funding Reached" becomes true immediately upon creation (amount_raised (0) >= goal (0)).
Instant Withdrawals: If the withdrawal logic relies on amount_raised >= goal, a malicious creator can create a campaign, convince users (via social engineering) to deposit funds, and then immediately withdraw those funds without ever having to achieve a legitimate target.
User Deception: It creates a deceptive campaign state that may confuse analytics tools or indexers that expect a non-zero denominator for progress bars.
Severity: Medium
Likelihood: Medium
Impact: Medium
Impact Details:
Immediate Rug Pull Risk: The mechanism designed to protect donors (holding funds until a goal is met) is nullified. The campaign is essentially a direct transfer wallet disguised as a crowdfunding contract.
Protocol Integrity: The business logic of the application is violated.
DoS/Griefing: Malformed campaigns clog the network state.
The following solana-program-test code proves that a campaign can be initialized with a goal of 0 and subsequently behave as if the funding goal is met immediately.
A strict invariant check must be added to the fund_create function to ensure goal is at least 1 (or a higher minimum depending on token decimals).
1. Locate fund_create:
Find the instruction handler responsible for initializing the campaign.
2. Add require! invariant:
Before assigning the goal to the account state, verify goal > 0.
Code Diff:
## Description The contract allows fund creation with a goal amount of zero, which could be misleading to contributors. ## Vulnerability Details The fund_create function doesn't validate that the goal amount is reasonable (greater than zero), allowing the creation of funds with meaningless fundraising goals. ```rust pub fn fund_create(ctx: Context<FundCreate>, name: String, description: String, goal: u64) -> Result<()> { let fund = &mut ctx.accounts.fund; fund.name = name; fund.description = description; fund.goal = goal; // No validation that goal > 0 fund.deadline = 0; fund.creator = ctx.accounts.creator.key(); fund.amount_raised = 0; fund.dealine_set = false; Ok(()) } ``` ## Impact Funds with zero goals could confuse contributors and potentially be used to trick users by making it unclear when the funding target has been reached. ## POC Add to tests/rustfund.ts: ```javascript //audit LOW - No Fund Goal Validation it("Can create a fund with zero goal", async () => { const zeroGoalFundName = "Zero Goal Fund"; const [zeroGoalFundPDA] = await PublicKey.findProgramAddress( [Buffer.from(zeroGoalFundName), creator.publicKey.toBuffer()], program.programId ); await program.methods .fundCreate(zeroGoalFundName, description, new anchor.BN(0)) .accounts({ fund: zeroGoalFundPDA, creator: creator.publicKey, systemProgram: anchor.web3.SystemProgram.programId, }) .rpc(); const fund = await program.account.fund.fetch(zeroGoalFundPDA); console.log(`Created fund with goal amount: ${fund.goal.toString()}`); }); ``` Output: ```javascript ======================================== 🐛 BUG REPORT [LOW]: No Fund Goal Validation ---------------------------------------- Description: The program allows creating funds with zero or invalid goal amounts Evidence: Created fund with goal amount: 0 ======================================== ``` ## Recommendations Add validation to ensure the goal is greater than zero: ```diff pub fn fund_create(ctx: Context<FundCreate>, name: String, description: String, goal: u64) -> Result<()> { // Validate goal is greater than zero + if goal == 0 { + return Err(ErrorCode::InvalidGoalAmount.into()); } let fund = &mut ctx.accounts.fund; fund.name = name; fund.description = description; fund.goal = goal; fund.deadline = 0; fund.creator = ctx.accounts.creator.key(); fund.amount_raised = 0; fund.dealine_set = false; Ok(()) } ```
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.