Fix inverted contribute/refund time gates in token-fundraiser (anchor)#614
Fix inverted contribute/refund time gates in token-fundraiser (anchor)#614boymak wants to merge 4 commits into
Conversation
The contribute and refund instructions gate on the campaign window with inverted comparisons: contributions were only accepted *after* the window had closed, and refunds were only allowed *while* the campaign was still open. The bug is masked by the tests using duration = 0, where both inverted conditions happen to evaluate correctly. - contribute now rejects once `elapsed_days >= duration` (campaign ended), so contributions are accepted while the campaign is open - refund now rejects while `elapsed_days < duration` (campaign still open), so refunds are allowed only after the campaign ends The tests now use a non-zero (open) campaign so contributions are valid, and assert that a refund is rejected while the campaign is still open. The same bug was fixed in the Pinocchio port of this example (PR solana-foundation#613), where the corrected behaviour was verified end-to-end on a local solana-test-validator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gn refund test - The refund-while-open test now asserts the caught error is `FundraiserNotEnded` instead of accepting any exception. - Adds a positive-path test that advances the bankrun clock past the campaign `duration` (via `banksClient.getClock()` + `context.setClock`) and asserts a successful refund: the contribution is returned, the vault is emptied, and the contributor account is closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — addressed both test-quality points in 063ec63:
That covers both sides of the corrected |
|
@boymak looks good, but there's some ci failures |
The bankrun runtime does not advance the clock on its own, so the day-based time gates depended on its default clock and failed in CI. Pin the clock relative to the fundraiser's recorded start time (`setElapsedDays`): one day into the campaign for the open-window tests, and past the duration for the successful-refund test. Also collapse the multi-line calls Biome flagged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks! Both CI failures are fixed now:
The program logic itself I verified end-to-end on a local Pushed to the branch — could you re-run the workflow when you get a chance? (fork PRs need approval to run.) |
Summary
The
token-fundraiser(Anchor) example gatescontributeandrefundon the campaign window with inverted comparisons:contributeonly accepted contributions after the campaign window had closed (require!(duration <= elapsed_days, FundraiserEnded)).refundonly allowed refunds while the campaign was still open (require!(duration >= elapsed_days, FundraiserNotEnded)).So for any real (non-zero duration) fundraiser, contributions are rejected for the entire campaign and only allowed once it's over, while refunds are allowed during the campaign and blocked afterwards — the opposite of the intended behaviour.
The bug is invisible today because the test uses
duration = 0, where both inverted conditions happen to evaluate correctly (0 <= 0and0 >= 0).Fix
contributenow rejects onceelapsed_days >= duration(campaign ended) → contributions accepted while the campaign is open.refundnow rejects whileelapsed_days < duration(campaign still open) → refunds allowed only after it ends.(The cast is parenthesised —
(… as u16) < duration— to avoid Rust parsingas u16 <as a generic.)Tests
The tests now create a non-zero (open) campaign so contributions are valid, and assert that a refund is rejected while the campaign is still open. A successful refund requires advancing the validator clock past
duration(e.g. via bankrun'ssetClock).Verification
The Anchor program compiles (
cargo build-sbf). The identical semantic fix was applied to the Pinocchio port of this example in #613, where the corrected behaviour was verified end-to-end on a localsolana-test-validator(contributions accepted while open, rejected after the campaign ends; refunds rejected while open).