Linear vesting payment streams (Sablier-style).
A payer locks a deposit that vests linearly to a recipient over [start, end). The recipient can withdraw
the vested-so-far amount at any time; either party can cancel, at which point the recipient keeps everything
vested up to that moment and the payer is refunded the unvested remainder. Many independent streams of one
token coexist, keyed by id — a minimal payroll / grant / subscription rail.
createStream(recipient, deposit, start, end)→id— pullsdepositand opens the stream. Crediting is the actual amount received (fee-on-transfer safe).vestedOf(id)— amount vested by now:0beforestart,deposit * (now - start) / (end - start)during,depositafterend(viaMath.mulDiv, floored).withdrawableOf(id)—vested - withdrawn, what the recipient can pull right now.withdraw(id, amount)— recipient pulls up to the withdrawable balance.cancel(id)— payer or recipient cancels: recipient getsvested - withdrawn, payer getsdeposited - vested. The stream settles to a zero remaining balance.
ReentrancyGuardon every token-moving entry point;SafeERC20; checks-effects-interactions (state is finalized before any transfer, including the two-leg cancel payout).- No stream can ever pay out more than was deposited; the vested curve is monotonic and clamps at the deposit.
- Fully tested: unit tests for the vesting curve, partial withdrawals, the cancel split (before/during/after
vesting), and every access guard, plus two stateful invariants (
fail_on_revert = true):- conservation — the contract's balance always equals the sum over all streams of their remaining
(
deposited - withdrawn), which drops to zero on cancel; - no-overpay — for every stream,
withdrawn <= deposited.
- conservation — the contract's balance always equals the sum over all streams of their remaining
(
forge test
FOUNDRY_INVARIANT_FAIL_ON_REVERT=true forge test --match-path "test/TokenStream.invariant.t.sol"TOKEN=0x... forge script script/Deploy.s.sol --rpc-url "$RPC_URL" --broadcastNot audited. Reference implementation — review before any real deployment.