VestingCovenant enforces linear or step-based token vesting schedules with cliff, pause/resume, cancel, and recipient transfer.
Contract path: contracts/core/streaming/VestingCovenant.cash
CashScript version: ^0.13.0
Constructor Parameters (Bytecode-Embedded)
| Parameter | Type | Description |
|---|---|---|
vaultId | bytes32 | Links to source vault |
senderHash | bytes20 | hash160(sender pubkey) — controls pause/cancel |
scheduleType | int | 1 = LINEAR_VESTING, 2 = STEP_VESTING |
totalAmount | int | Total tokens or BCH to vest |
startTimestamp | int | When vesting begins (unix seconds) |
endTimestamp | int | When fully vested |
cliffTimestamp | int | No claims before this time. 0 = no cliff. |
stepInterval | int | Seconds per step (STEP_VESTING only, else 0) |
stepAmount | int | Tokens per step (STEP_VESTING only, else 0) |
NFT State (40 bytes)
Transaction Structure
All entrypoints spend vesting state fromtx.inputs[0].
| Function | Required outputs | Notes |
|---|---|---|
claim | tx.outputs[0] recipient payout, tx.outputs[1] updated vesting UTXO | Claimable amount is computed on-chain. |
complete | No continuation output required | Optional tx.outputs[0] payout of remaining amount to recipient. |
pause | tx.outputs[0] updated vesting UTXO | Stores pause start timestamp. |
resume | tx.outputs[0] updated vesting UTXO | Shifts vesting cursor forward by pause duration. |
cancel | If claimableNow > 0: tx.outputs[0] recipient payout. If unvested > 0: tx.outputs[1] sender payout | Cancellation consumes covenant UTXO. |
transfer | tx.outputs[0] updated vesting UTXO | Rewrites mutable recipient hash. |
Functions
claim(sig recipientSig, pubkey recipientPubkey)
Recipient claims all currently vested and unclaimed tokens.
Validates: recipient signature, status == ACTIVE, cliff passed, claimable > 0
Vested amount:
- LINEAR:
vestedTotal = (totalAmount × elapsed) / duration - STEP:
vestedTotal = (elapsed / stepInterval) × stepAmount claimable = vestedTotal - total_released
newTotalReleased >= totalAmount → status = COMPLETED
complete()
Permissionless. Forces completion after endTimestamp. Distributes any remaining unvested/unclaimed amount to recipient.
Validates: tx.locktime >= endTimestamp
pause(sig senderSig, pubkey senderPubkey)
Sender-only. Requires FLAG_CANCELABLE. Records pause_start = tx.locktime.
resume(sig senderSig, pubkey senderPubkey)
Sender-only. Advances cursor by pause duration: newCursor = cursor + (tx.locktime - pause_start).
cancel(sig senderSig, pubkey senderPubkey)
Sender-only. Requires FLAG_CANCELABLE. Splits at cancellation time:
- Vested (unclaimed) → recipient
- Unvested → sender
- If
claimableNow > 0, recipient payout is enforced attx.outputs[0]. - If
unvested > 0, sender payout is enforced attx.outputs[1].
transfer(sig recipientSig, pubkey recipientPubkey, bytes20 newRecipientHash)
Recipient-only. Requires FLAG_TRANSFERABLE and status == ACTIVE. Updates recipient_hash in NFT state.
