A Kaspa covenant locks funds behind a script — a set of rules written at deployment time that dictate exactly how the UTXO can be spent. Some of those rules require a signature from a specific key. Others, called keyless paths, can be broadcast by anyone: after a delay expires, after an inheritance timer runs out, after a dispute window closes.
That openness is a feature. It is also an attack surface.
When a keyless path fires, the person who constructs and submits the transaction controls something dangerous: how much of the UTXO's value goes to the network as a fee. Without a guardrail, a griefing actor could build a transaction that honors the covenant's output rules — correct destination, correct minimum value — but sets an absurdly high network fee, burning the rest. The covenant's beneficiary gets their slice; the rest evaporates into miner rewards.
This is the class of attack that the feeBudget parameter exists to prevent.
feeBudget — a constructor parameter injected into a Kaspa covenant at deployment. It caps the maximum value the covenant tolerates losing on any single keyless transaction beyond the mandatory output. It bounds both network fees and any residual directed to extra outputs.
How feeBudget enters the script
Both core covenants in the Kaspa Forge platform accept feeBudget as a constructor argument. In the vault contract (vault.sil, deployed on Kaspa Toccata mainnet), it's the 9th parameter. In the escrow contract (escrow.sil), it's the 11th. The Silverscript compiler bakes this value directly into the deployed bytecode — once the covenant exists on-chain, the budget is immutable.
Within the script, most spending paths enforce a check along these lines:
require(feeBudget > 0 && feeBudget <= 10_000_000)
The constant 10_000_000 sompi equals 0.1 KAS. It's the platform-wide ceiling, defined as MAX_FEE_BUDGET. No keyless path on any Kaspa Forge covenant can burn more than 0.1 KAS beyond its required output — whether that value becomes a network fee or leaks into an additional transaction output.
Why "residual" matters alongside fees
The v3 covenant paths verify the required output — they check that outputs[0] pays the correct destination with at least a certain minimum value. But they do not currently check tx.outputs.length. This means the transaction author could append a second output, sending up to feeBudget worth of residual value to an address they control, rather than paying it as a network fee.
The griefing cap still holds — the total loss is bounded by feeBudget regardless of where the residual goes. But it's a documented limitation of v3: the bound works on total loss, not specifically on fee amount. Kaspa Forge's official transaction builders always construct a single canonical output, so in practice the residual is always a network fee. A stricter output-count guard is targeted for the v4 contract revision.
The platform-wide scorecard: 16 of 17
Across the two core covenants, Kaspa Forge deploys 17 spending paths in total:
| Covenant | Paths | feeBudget enforced | Exception |
|---|---|---|---|
| vault.sil (Kaspa Safe) | 7 | 6 of 7 | migrate — no cap |
| escrow.sil (Kaspa Escrow) | 10 | all 10 | — |
| Total | 17 | 16 | 1 exception |
That single exception — migrate — is deliberate.
Why migrate escapes the cap
migrate(hotSig, alarmSig) requires both the hot key and the alarm key to sign the transaction. With dual authorization, the owner has unbounded control: the path can redirect funds to any address, in any split, with any fee. A griefing cap here would accomplish nothing — if both keys are compromised, the attacker already has full power, and a 0.1 KAS fee ceiling won't save the vault.
What migrate gives back is upgradeability. A vault owner can move funds from a v3 covenant to a v4 covenant in a single transaction, rotate the hot key, or perform an emergency exit — all with the same two signatures that normally guard the vault. This is how Kaspa Safe will roll out tighter output-count guards without requiring users to close and recreate their vaults.
Layered defense: feeBudget is not alone
The fee cap is one layer in a stack of invariants that reinforce each other. Here's how the layers fit together:
Layer 1 — Single-input invariant. Every one of the 17 paths opens with require(tx.inputs.length == 1). This prevents a related attack: merging two covenant UTXOs at the same P2SH address in a single transaction, where the surplus from the smaller UTXO leaks into fees or side outputs. The single-input rule makes each UTXO spend completely independent — no cross-contamination.
Layer 2 — feeBudget cap. Limits the maximum economic loss per keyless spend to 0.1 KAS. This article's main subject.
Layer 3 — Compute budgets. Each spending path runs under a separate limit on script complexity — the number of signature checks and introspection operations the verifier must perform:
vault normal paths: COMPUTE_BUDGET = 20 (single checkSig + introspection)
vault migrate: MIGRATE_BUDGET = 30 (two checkSig, measured min ~20, with headroom)
escrow mutual: ESCROW_BUDGET = 40 (up to 3 checkSig + 3-output introspection)
On Kaspa's high-throughput chain running at 10 blocks per second, miners evaluate thousands of transactions per second. Unbounded script complexity would be a denial-of-service vector. Compute budgets prevent it.
Layer 4 — Value conservation. The escrow contract's arbitrate paths enforce an explicit bound: out0 + out1 >= in - feeBudget. This ensures that the two outputs (buyer's share + seller's share) account for the covenant's full input minus the allowed budget. No value is "lost" to phantom outputs.
Together:
| Defense | Prevents |
|---|---|
| Single-input | Cross-UTXO value siphon |
| feeBudget cap | Excess fee or residual on a single spend |
| Compute budget | Script-complexity denial of service |
| Value conservation | Output manipulation on multi-output paths |
| Key separation | Single-compromise unilateral drain |
No single layer is sufficient. The stack only works because each invariant closes a gap the others leave open.
On-chain vs. off-chain enforcement
The on-chain script enforces the upper bound: feeBudget <= 10_000_000 (0.1 KAS). But Kaspa Forge's off-chain builders — the WASM core running in the user's browser — apply a tighter range: [1,000,000 — 10,000,000] sompi (0.01–0.1 KAS).
The lower bound is purely off-chain policy. On-chain, the script only requires feeBudget > 0. A third-party deployment could create a covenant with a feeBudget of 1 sompi — technically valid, but practically useless, since no miner would accept a transaction with a fee below the relay minimum.
This split is intentional. The contract is a public, auditable artifact: anyone can read the bytecode and verify the invariant. Kaspa Forge's builders add usability constraints on top without restricting what others might build on the same Toccata covenants. Kaspa's transaction fee mechanics — where nodes set relay minimums independently of mining policy — make this lower bound a practical necessity rather than a protocol-level guarantee.
What this looks like in a live vault
When you create a vault through Kaspa Safe, the feeBudget defaults to a value within the supported range. Every keyless path in that vault — complete() after the withdrawal delay, inheritAuto() after the inheritance timer — enforces the cap. If Kaspa Safe's watcher, or any third-party tool, or any random observer broadcasts one of these transactions, the maximum loss to fees plus residual is at most 0.1 KAS, regardless of how much KAS the vault holds.
The same cap applies to every Kaspa Escrow deal. The autoRelease() path (optimistic release after the dispute window, which the watcher broadcasts automatically) and the timeoutToBuyer()/timeoutToSeller() paths (dead-arbiter protection, the only escrow paths that charge zero service fee) all carry the feeBudget constraint. The timeout paths exist precisely for the scenario where the service itself disappears — and their feeBudget cap ensures that even a hostile broadcaster can't exploit that moment of vulnerability.
The off-chain guardrail in practice
Kaspa Forge's WASM core (kaspa-safe-core) rejects feeBudget values outside the supported range before the transaction is ever signed or submitted. This means:
feeBudget < 1_000_000: rejected at build time (would create an un-broadcastable keyless path).feeBudget > 10_000_000: rejected at build time (would violate the on-chain cap).feeBudget in [1_000_000, 10_000_000]: accepted; the keyless path is both valid and executable.
A vault or escrow created through Kaspa Forge will always sit within this band. A vault created by other means — using the open-source contract bytecode directly — could technically have any positive feeBudget, but would be subject to the on-chain ceiling of 0.1 KAS regardless.
Trade-offs and known boundaries
v3 output-count gap. As noted above, v3 paths check output value but not output count. The 0.1 KAS bound holds, but within that bound, residual can flow to an extra output rather than miners. This is a cosmetic issue for Kaspa Forge (our builders are canonical), but a real concern for third-party integrators. v4 will close it.
feeBudget is not a fee oracle. The cap doesn't set the actual network fee — it only limits how much the covenant is willing to lose. The actual fee is determined by the transaction builder based on current network conditions. Kaspa Forge queries the node's fee-estimate endpoint and constructs transactions with appropriate fees, well within the budget.
100 sompi dust limit. Kaspa's protocol enforces a minimum output value (dust limit). The feeBudget cap doesn't interact with this — the required output already exceeds dust by definition (vault outputs go to P2PK/P2SH addresses, escrow outputs have a minimum of 1 KAS per the split guard). But it's worth noting that feeBudget doesn't protect against dust-limit edge cases; that's handled separately by the output value checks in each path.
Escrow's mutual path has the highest compute budget — 40, compared to 20–30 for vault paths. This reflects its complexity: up to three signature checks (buyer + seller + fee output verification) and three-output introspection. The feeBudget cap applies identically; the compute budget is the only thing that's larger.
What this means for your funds
If you hold KAS in a Kaspa Safe vault or use Kaspa Escrow for a P2P deal, the feeBudget cap is one of several invisible guarantees protecting your funds between the moment a keyless path becomes valid and the moment the transaction lands on-chain. You never see it in the UI — it's baked into the contract bytecode at deployment time, enforced by every node that validates the transaction.
The 0.1 KAS cap is deliberately generous: it covers realistic network fees under normal and congested conditions without leaving room for meaningful economic damage. On Kaspa's high-throughput chain, where the fee per UTXO is modest and blocks arrive every second, 0.1 KAS provides comfortable headroom while keeping the griefing surface negligible.
Kaspa Safe's covenant vault uses the feeBudget defense described here alongside single-input enforcement and compute budgets — all transparent in the open-source contract. On-chain operations are free forever; the optional Telegram alarm service costs 100 KAS/year with a 30-day free trial. Create a vault →
---
*This article covers the feeBudget mechanism as implemented in vault.sil v3 and escrow.sil on Kaspa Toccata mainnet. Contract bytecode and builder source are published at github.com/Kaspaforge/kaspaforge. For the broader covenant system, see how Kaspa covenants work.*
FAQ
What is feeBudget in a Kaspa covenant?
A constructor parameter baked into the deployed script at creation time. It represents the maximum value the covenant tolerates losing on any single keyless transaction beyond the required output — capping both network fees and any residual sent to extra outputs.
Why is the vault's migrate path exempt from the feeBudget cap?
migrate requires both the hot key and the alarm key simultaneously. With both signatures present, the owner has full authority; a griefing cap would restrict upgrade flexibility without adding meaningful security.
Can a griefing actor drain my Kaspa Safe vault through inflated fees?
No — not beyond the feeBudget cap, which tops out at 0.1 KAS (10,000,000 sompi). Keyless paths like complete() and inheritAuto() enforce this bound on-chain in the script itself.
Does feeBudget also protect Kaspa Escrow deals?
Yes. All 10 escrow spending paths enforce feeBudget, including the autoRelease optimistic path and the timeout paths that fire when no arbiter is available.
What is the difference between feeBudget and a compute budget?
feeBudget limits economic loss — the maximum value that can leave the covenant as fees or residual. Compute budget limits script execution complexity — the number of signature checks and introspection operations allowed during verification.
What happens if I set feeBudget too low?
The on-chain script only requires feeBudget > 0, so a micro-budget is technically valid. But it makes keyless paths un-broadcastable: miners reject transactions whose fee is below the relay minimum. Kaspa Forge's builders enforce a floor of 0.01 KAS to prevent this.
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
