solana-foundation / solana-foundation/program-examples
token-fundraiser: no teardown for a failed campaign (vault + fundraiser rent stranded)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.4k
- Forks
- 543
- Avg merge
- 21h 7m
- Merged PRs (30d)
- 14
Description
Summary
The token-fundraiser example 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 #708)
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_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. - 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: https://github.com/solana-foundation/program-examples/pull/708#discussion_r3889947500
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in tokens/token-fundraiser/anchor and tokens/token-fundraiser/pinocchio, reading the existing refund and check_contributions instructions and their account-closing behavior. Trace how expiry, the recorded target, and remaining vault tokens are handled. Done means both implementations expose a maker-callable failed-campaign teardown that sweeps the vault and closes the vault and fundraiser accounts after expiry without meeting the target.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- blockchain
- Domain
- blockchain
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100