A savings commitment device with real stakes. You commit to save target of a token by deadline.
Hit the target and wait until the deadline → withdraw everything, penalty-free. Bail early, or fall short
at the deadline → you can still get your money out, but a penaltyBps cut goes to a beneficiary you
chose up front (a friend, a cause, or an "anti-charity" you'd hate to fund). The looming penalty is the
motivator — StickK/Beeminder on-chain. MIT.
createGoal(token, target, deadline, penaltyBps, beneficiary, initialDeposit)— open a goal (penalty capped at 50%). Funds credited actual-received (fee-on-transfer-safe).deposit(id, amount)— add toward the target (anyone can top up; it's the owner's to withdraw).withdraw(id)— owner-only, once. Penalty-free only ifbalance >= targetand the deadline has passed; otherwisepenaltyBpsof the balance goes to the beneficiary and the rest to the owner.quoteWithdraw(id)/isSuccess(id)— preview the split / check the penalty-free state.
- Solvency — the vault's balance always equals the sum of every un-withdrawn goal's balance, across
any sequence of create / deposit / withdraw / warp (12,800 calls, 0 reverts). A withdrawal splits
exactly into
toOwner + penalty == balance— no dust created or lost. Mutation-checked (paying the owner the full balance, skipping the penalty, fails the penalty tests); reentrancy-safe (terminal state set before transfers, CEI +nonReentrant).
- Only the goal owner (subject to the rules) and their chosen beneficiary ever receive funds. No admin, no fees, no path to another goal's money.
- The commitment is self-imposed and self-enforcing — the contract can't check whether you actually did the real-world thing; it enforces the on-chain saving target and the penalty. Choose a beneficiary that genuinely motivates you. Standard ERC-20 assumed. Unaudited reference implementation.
forge test # 13 tests: success/early/missed withdrawals, conservation, isolation, reentrancy, solvency invariant