A promise with a clock in it
What happens inside a vault, from lock to claim.
The life of a vault
1. Lock
The funder deposits the full amount into the vault.
2. Wait
Nothing to do. Anyone can see what unlocks when.
3. Unlock
As dates pass, funds unlock. No transaction needed.
4. Claim
The recipient withdraws what has unlocked, whenever they like.
Three schedules, one example
One 12,000 tUSDC grant, released three ways.
On a date
unlocked(t) = t ≥ date ? total : 0
12,000 on day 365
Monthly steps
Equal tranches; rounding leftovers go to the last one.
1,000 on the same day each month
Gradually, with a cliff
After the cliff: total × (t − start) ÷ (end − start).
3,000 at month 3, then about 33 a day
Cliffs, revocation and reviewers
- Cliff
- A waiting period before anything unlocks.
- Revocation
- Unlocked funds stay claimable; only the still-locked part returns to the funder.
- Reviewer approval
- N of M named reviewers must approve before anything is claimable.
Diagram: revoking at month 5 of a 12-month grant. Unlocked stays with the recipient, the rest returns to the funder.
Why recipients claim, instead of being paid
A contract can't move money on its own. Claiming lets the recipient pick the moment; a missed month simply waits.
Time on a blockchain
Contracts read the block timestamp, accurate to seconds. The demo's clock can fast-forward; Reset demo returns to today.
For developers
The demo's data layer is shaped like the contract, so real chain calls wouldn't touch the UI.
Show the contract interface
Contract interface (sketch)
interface ITimeVault {
struct Schedule { uint8 kind; uint64 start; uint64 cliff; uint64 end; uint16 steps; }
/// Deploys a vault and pulls `total` of `token` from the funder.
function lock(address recipient, IERC20 token, uint256 total,
Schedule calldata schedule, bool revocable,
address[] calldata reviewers, uint8 required)
external returns (address vault);
function unlocked(uint64 at) external view returns (uint256);
function claimable() external view returns (uint256);
function claim() external; // recipient only
function approve() external; // named reviewers only
function revoke() external; // funder only, if revocable
event Locked(address indexed funder, address indexed recipient, uint256 total);
event Claimed(address indexed recipient, uint256 amount);
event Approved(address indexed reviewer, uint8 count);
event Revoked(uint256 kept, uint256 returned);
}How the demo mirrors it
- src/lib/demo/vesting.ts
- unlocked() and claimable(), as pure functions.
- src/lib/demo/ops.ts
- lock, claim, approve, revoke state changes.
- src/lib/demo/chain.ts
- Simulated wallet prompt, pending, confirmed or reverted.
- src/lib/demo/store.ts
- Saved state and the demo clock.