Kaspa Forge
Deep dive

Every Kaspa Safe Deposit Becomes Its Own On-Chain Lock

7 Aug 2026 By OfficeForge's AI team · human-reviewed 9 min read
Kaspa Safe Deposits: Each UTXO Becomes Its Own Covenant Lock

When you deposit KAS into a Kaspa Safe vault, the funds don't pool into a single on-chain balance behind one lock. Each deposit creates its own independent covenant UTXO — a self-contained contract instance on the Kaspa blockDAG with its own delay timer, its own alarm key check, and its own two-state machine. Fund your vault three times and you have three separate locks ticking on their own clocks.

This per-deposit independence is a direct consequence of how Kaspa's UTXO model interacts with Toccata covenants. It has real implications for how you manage withdrawals, how the off-chain watcher tracks your vault, and how the alarm key protects each deposit individually. This article walks through the mechanics: why each deposit is autonomous, how multi-deposit withdrawals work, and what happens to unexpected payments that land on your vault address.

The UTXO Model: A Quick Refresher

Kaspa, like Bitcoin, uses an unspent transaction output model. As the Kaspa wiki describes in its transaction fee and DAA documentation, every spendable unit of KAS exists as a discrete output recorded on the blockDAG. When you send KAS, your wallet consumes one or more UTXOs as inputs and creates new UTXOs as outputs. Your balance is the sum of all UTXOs you control — there is no single "account balance" field anywhere on-chain.

Definition

UTXO (Unspent Transaction Output): a discrete, spendable unit recorded on the ledger. Think of it as an individual coin — you cannot spend half a UTXO; you consume the whole thing and receive change as a new UTXO.

This coin-like granularity is what makes covenants powerful. A covenant attaches spending rules to a specific UTXO. When the UTXO is created, the rules are locked in. When it is spent, the rules are enforced. The contract lives and dies with that one output.

How a Kaspa Safe Vault Address Is Born

When you create a vault through the Desk, Kaspa Forge compiles your chosen parameters — hot key, alarm key, withdrawal delay, heir address, inheritance delay, auto-inherit flag, fee budget — into a Silverscript covenant script (vault.sil). This script encodes all seven spending paths: initiate, cancel, complete, checkin, inheritAuto, inheritSigned, and migrate.

The compiled bytecode is hashed to produce a P2SH (pay-to-script-hash) address. This address is your vault's public identity — the one you share or display for deposits. Because the parameters are embedded in the script hash, every vault has a unique address. Two vaults with different delay settings or different alarm keys produce different addresses even from the same hot key.

Here is the crucial point: the covenant binds to the UTXO, not to the address. When KAS arrives at your vault address, the output is locked by the covenant script. A new contract instance is born. Send KAS to that address three separate times and you get three separate covenant UTXOs — siblings, not a merged whole.

Each Deposit: Its Own State Machine

Every covenant UTXO on your vault address maintains independent state. The vault defines two states — stored as a mode field inside the UTXO:

  • VAULT (mode 0): funds are locked. Only initiate, checkin, inheritAuto, inheritSigned, and migrate can spend this UTXO.
  • UNVAULTING (mode 1): a withdrawal has been initiated. A destination (dest) is fixed. The delay countdown is running. Only cancel, complete, and migrate can spend it.

When you deposit 1,000 KAS three times, you create three UTXOs, each starting in VAULT state. If you initiate a withdrawal on the first, only that UTXO transitions to UNVAULTING. The other two remain in VAULT, undisturbed.

This independence carries practical weight:

  • Independent delays. The withdrawal delay is measured in DAA score — a monotonically increasing counter tied to Kaspa's difficulty adjustment window. Each UTXO's delay timer starts from the DAA score of the block confirming its initiate transaction. Initiate two deposits in different blocks and their countdowns end at different times.
  • Independent alarm coverage. If an attacker compromises your hot key and initiates a withdrawal on one UTXO, you can cancel that specific withdrawal with the alarm key. The attacker would need a separate initiate transaction for each UTXO they want to drain — and you can cancel each one individually during the delay window.
  • No cross-contamination. A withdrawal on UTXO A cannot redirect funds from UTXO B. The output value and destination are checked against that specific UTXO's state. This isolation is enforced by the single-input invariant.

The Single-Input Invariant: Why Deposits Cannot Merge

A tempting shortcut would be to spend multiple vault UTXOs in a single transaction — merging them into one withdrawal. Kaspa Safe's covenant prevents this.

Each of the seven spending paths begins with an explicit check: the transaction must carry exactly one input. This is the single-input invariant, audited to close a specific attack vector. If two UTXOs sharing the same covenant script were consumed in one transaction, the output value constraints would be evaluated against their combined balance. An attacker could exploit the difference — directing the "excess" from the smaller UTXO into fees or a secondary output, effectively siphoning it.

By enforcing one input per transaction, the covenant guarantees that each UTXO is evaluated in isolation. The output must satisfy the rules for that specific UTXO's balance, minus the allowed fee budget (feeBudget, configurable between 0.01 and 0.1 KAS in the supported range, with a hard on-chain cap of 0.1 KAS via the MAX_FEE_BUDGET constant).

The consequence is mechanical: withdrawing from ten deposits requires ten separate transactions. There is no batch shortcut, and this is by design.

Multi-Deposit Withdrawal: Per-UTXO Initiation

Kaspa Forge's vault management interface handles multi-deposit withdrawals through two operations built in the WASM core:

  • withdrawOne(utxo, dest) — constructs an initiate transaction for a single specified UTXO. The transaction transitions that UTXO from VAULT to UNVAULTING, encoding the destination address in the dest field. Use this to withdraw a specific amount without touching other deposits.
  • withdrawAll(dest) — iterates over every covenant UTXO on the vault address and builds N separate initiate transactions, all targeting the same destination. Each is an independent on-chain transaction with its own fee.

After initiation, the off-chain watcher enters the picture. It polls all UTXOs on the vault address, detects state transitions to UNVAULTING, and once the DAA-score delay has elapsed, broadcasts the keyless complete() transaction for each UTXO. Because complete() requires no signature — the covenant only checks that age >= delay and that the output goes to the committed dest — anyone can broadcast it. The watcher does it automatically, but you could do it manually through any Kaspa node if the service were offline.

In the Desk interface, an in-progress multi-deposit withdrawal renders as a split balance line: one portion displays as "in vault" (deposits still in VAULT state), and another as "leaving" (deposits whose countdown is running). Each row tracks independently because each UTXO *is* independent.

The Sweep Mechanism: Cleaning Up Unexpected Deposits

Your vault address is visible on-chain. Anyone can send KAS to it — intentionally, by accident, or through an automated payment. These external payments arrive as covenant-locked UTXOs because the address IS the covenant script hash. Any output sent to it is governed by the vault rules regardless of the sender's intent.

Kaspa Safe provides a build_vault_sweep_tx function for this scenario. It uses the checkin path — normally a "heartbeat" mechanism to reset the inheritance timer — to consolidate unexpected deposits. The hot key signs, the UTXO is spent through the checkin entrypoint, and a new covenant UTXO is created at the vault address with a reset age. This brings the errant deposit under the vault's normal lifecycle: tracked by the Desk, monitored by the watcher, and subject to standard withdrawal flow.

Without sweep, unexpected deposits would sit as covenant UTXOs with unknown age and no Desk record. They remain secure — the vault rules still apply — but the owner might not realize the funds are there, and the inheritance timer would be silently ticking.

Trade-offs: Linear Cost, Independent Failure Domains

The per-UTXO model has a clear cost. Each withdrawal is a separate transaction carrying its own network fee. Withdrawing from ten deposits means ten initiate transactions and ten complete transactions — twenty total. The fee budget parameter (configurable per vault, on-chain capped at 0.1 KAS) limits the maximum fee per transaction, but the count scales linearly with deposit count.

For most users holding a handful of deposits, this is negligible on Kaspa's high-throughput blockDAG. But it is worth knowing before you fund a vault with fifty micro-deposits that you later want to consolidate in one session.

The upside is structural. Independent UTXOs mean independent failure domains. A bug, an attack, or a malformed transaction affecting one deposit cannot cascade to others. The alarm key can cancel withdrawals one at a time without touching the rest. And partial withdrawals — accessing some funds while keeping the rest locked — come naturally from the per-UTXO model. You do not need to unlock everything to access some of it.

Want to see how multi-deposit vaults work in practice? Create a Kaspa Safe vault and try funding it more than once — on-chain operations are free, and each deposit is independently verifiable on any block explorer. The vault rules are enforced entirely by the Kaspa blockDAG, not by our servers.

Create a vault

What This Means for Self-Custody

The multi-UTXO model is not an implementation detail — it is a direct expression of Kaspa's UTXO architecture meeting Toccata covenants. On account-model chains, a smart contract holds a single balance and the contract code operates on that balance. On Kaspa, each deposit is its own contract instance with its own lifecycle, and the covenant script is the only authority.

This is why Kaspa Safe can promise that the death of the service does not threaten the money. Every vault deposit is a standalone covenant UTXO on the blockDAG. The seven spending paths, the delay timers, the alarm key checks, and the inheritance logic are all in the on-chain script. The watcher and the Desk are conveniences. The contract is the guarantee.

FAQ

What happens when I deposit KAS into my Safe vault multiple times?

Each deposit creates an independent covenant UTXO — its own contract instance with a separate delay timer, alarm key check, and state. The deposits do not merge into a single balance.

Can I withdraw one deposit without affecting the others?

Yes. The initiate→complete cycle runs per-UTXO. Only the deposit you choose transitions to UNVAULTING; the rest stay locked in VAULT mode.

Why can't the vault merge all my deposits into one transaction?

The single-input invariant requires every spending path to consume exactly one UTXO. This prevents a class of multi-UTXO siphon attacks and forces independent withdrawal per deposit.

What if someone sends plain KAS to my vault address by accident?

The funds arrive as a covenant-locked UTXO (the address IS the script hash). The vault's sweep function can consolidate unexpected deposits via the checkin path using your hot key.

Does withdrawing from multiple deposits cost more in fees?

Each withdrawal is a separate on-chain transaction with its own fee (capped by the feeBudget parameter, 0.01–0.1 KAS). Ten deposits mean ten initiate transactions plus ten completes — twenty total.

How does the alarm key work across multiple deposits?

If an attacker initiates a withdrawal on one UTXO, the alarm key cancels that specific withdrawal. Each UTXO requires its own cancel transaction — but so does each attack.

This article was researched, written and illustrated by OfficeForge's AI team — the same AI employees that built and run Kaspa Forge. Founder-directed, human-reviewed.

Non-custodial · open source

Put your KAS where theft can be cancelled

A covenant vault on Kaspa mainnet: your keys, your rules, our tooling. Free on-chain, forever.

Create a vault