The contribute function fails to record the contributor’s deposited amount in their Contribution account, causing a silent logic failure. The contribution transaction succeeds (no error), but the Contribution.amount remains zero. This bug effectively bypasses the refund mechanism, since the contract has no record of the contribution amount to refund. Contributors who funded a campaign cannot get their money back via the refund instruction because the contract thinks they contributed 0.
In the contribute instruction handler, the code updates the campaign’s total funds raised (fund.amount_raised) but never updates the contributor’s own contribution amount. For example, after a user contributes 5 SOL, the global Fund state reflects the increase, but the corresponding Contribution account still shows amount = 0. This is a silent failure – the transaction doesn’t revert, so from the user’s perspective the contribution appears to have succeeded, when in reality the contract state for that contributor is incorrect.
It likely remains at the default value 0 because the program did not set it during initialization or subsequent contributions. Consequently, any logic that relies on Contribution.amount will not function as intended. One such logic is the refund process. The refund instruction presumably uses the stored contribution amount to determine how much SOL to return to the contributor. Because of this bug, when a contributor attempts to refund, the program will see their recorded contribution as 0 and thus transfer 0 lamports back. The code does not throw an error; it simply does nothing (transferring zero lamports is effectively a no-op), silently bypassing the intended refund payout. The contributor’s refund attempt will "succeed" from the program’s perspective but result in no funds returned.
This flaw breaks the core promise of the crowdfunding platform: contributors to failed campaigns cannot actually reclaim their funds. In a failure scenario (goal not met by deadline), contributors expect to call refund to get their money back. However, due to the untracked contribution amount, the contract will not return any funds (since it believes the contribution was 0). This means contributors’ funds remain locked in the campaign fund, even after the campaign fails, unless the campaign creator manually intervenes (which is against the protocol’s intent). Furthermore, this silent failure could be exploited in combination with other issues. For instance, a malicious campaign creator could deliberately leave this bug unfixed to deny refunds to contributors and then withdraw those "stuck" funds for themselves. Contributors have no on-chain evidence of their contribution amount, so the contract cannot enforce fair refunds. This issue severely damages user trust and effectively results in loss of funds for honest contributors in the event of a campaign failure.
Manual code analysis (viewing lines with initialization and updating of the Contribution.amount field).
Fix the function so that contribution.amount = amount is set on the first call, and addition is performed on repeated calls (for example, contribution.amount += amount with overflow check).
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.