Summary
The token-fundraiser example has no teardown path for a failed campaign (one that expires without meeting its target). This affects both implementations:
Details
The program exposes four instructions — initialize, contribute, check_contributions (the anchor checker), and refund — and only tears down accounts on the success path:
- Success:
check_contributions requires vault.amount >= amount_to_raise, transfers the full vault.amount to the maker, and closes the vault + fundraiser. Any tokens sent directly into the vault are swept to the maker here, so nothing is stranded on this path.
- Failure: once a campaign expires under target, contributors call
refund, which returns each contributor's recorded amount and closes only that contributor's record. There is no instruction that closes the vault or the fundraiser account, so:
- the vault ATA and fundraiser PDA are left behind with their rent unrecovered, and
- any tokens transferred directly into the vault (not via
contribute, so not part of any recorded contribution) stay permanently locked.
This gap is inherited from the reference implementation — the anchor version has the same four instructions and no failed-campaign teardown — so it is not specific to the pinocchio port.
Why not just gate on the vault balance
Switching check_contributions back to releasing based on the raw vault balance is not the fix: because anyone can transfer into a standard ATA, that would let a stranger force-release a campaign that never met its target (and block legitimate refunds). That is a more serious issue than stranded rent, so both implementations intentionally gate release on the recorded total.
Proposed follow-up
Add a maker-callable teardown instruction for a failed campaign — allowed once the duration has elapsed and the target was not met — that sweeps any remaining vault balance back to the maker and closes the vault + fundraiser accounts, recovering their rent. To keep the examples in parity, this should be applied to both the anchor and pinocchio implementations together rather than diverging one.
Context
Raised during review of #708: #708 (comment)
Summary
The
token-fundraiserexample has no teardown path for a failed campaign (one that expires without meeting its target). This affects both implementations:tokens/token-fundraiser/anchortokens/token-fundraiser/pinocchio(added in feat(token-fundraiser): add pinocchio example #708)Details
The program exposes four instructions —
initialize,contribute,check_contributions(the anchorchecker), andrefund— and only tears down accounts on the success path:check_contributionsrequiresvault.amount >= amount_to_raise, transfers the fullvault.amountto the maker, and closes the vault + fundraiser. Any tokens sent directly into the vault are swept to the maker here, so nothing is stranded on this path.refund, which returns each contributor's recorded amount and closes only that contributor's record. There is no instruction that closes the vault or the fundraiser account, so:contribute, so not part of any recorded contribution) stay permanently locked.This gap is inherited from the reference implementation — the anchor version has the same four instructions and no failed-campaign teardown — so it is not specific to the pinocchio port.
Why not just gate on the vault balance
Switching
check_contributionsback to releasing based on the raw vault balance is not the fix: because anyone can transfer into a standard ATA, that would let a stranger force-release a campaign that never met its target (and block legitimate refunds). That is a more serious issue than stranded rent, so both implementations intentionally gate release on the recorded total.Proposed follow-up
Add a maker-callable teardown instruction for a failed campaign — allowed once the duration has elapsed and the target was not met — that sweeps any remaining vault balance back to the maker and closes the vault + fundraiser accounts, recovering their rent. To keep the examples in parity, this should be applied to both the anchor and pinocchio implementations together rather than diverging one.
Context
Raised during review of #708: #708 (comment)