Evidence

What is proven about this
covenant, and by what.

Nobody has ever audited a Kaspa covenant — asked in the core developer channels, and the answer was nobody. There is no certificate to buy, so the only honest position is to publish the evidence that does exist and name precisely what it is not. This page is that, including the part where six vulnerabilities were found in here.

Three layers, and one that is missing

Each proves something the others cannot.

A rule can be right in the specification and wrong in the bytecode. Bytecode can be right in an engine and unrelayable on a real network. So the same claim is made three times, in three places that can disagree with each other.

Layer 1 · the rules

One implementation of every limit

606 tests across nine packages. Every verdict any package gives comes from @warda_protocol/core, so a second copy of the rules cannot drift from the first. This proves what the limits mean. It proves nothing about the script that enforces them.

Layer 2 · the bytecode

The engine a Kaspa node validates with

14 probes run the compiled covenant through TxScriptEngine from rusty-kaspa — the same engine a node uses, not a model of it. Eight the covenant must refuse and six it must accept, each refusal beside its nearest legitimate twin, all green against the deployed version. A second suite — 33 tests flipping one field at a time from an accepted baseline — is green too, as of 15 September 2026.

Layer 3 · the chain

Transactions that actually happened

Grants created, spent, delegated, settled, revoked and reclaimed on testnet-10, with transaction ids published on the agent pages. This catches what the engine alone cannot: fees, storage mass, relay policy, and every assumption about the network that was never written down.

Headroom · measured, not estimated

What the covenant costs a node to run

Read out of the running engine at the widest allowlist depth: 8,680 bytes of a 1,000,000-byte ceiling, 16 compute units of 65,535, and 118 stack slots of 244. Stack is the tightest at 2.07× and it is flat — the Merkle fold reuses slots, so a 65,536-payee allowlist costs the same stack as a four-payee one.

Layer 4 · formal methods

Not done

Absent. The state machine — budget, per-spend cap, epoch headroom, allowlist membership, succession arithmetic — has not been modelled in an SMT solver. Testing samples that space; a solver covers it. Worth saying plainly, because a page like this is where a reader would assume otherwise.

One of these two suites was dead, and writing this page is what found it. Running covenant/harness to collect figures for the panel above returned constructor argument count mismatch on 32 of its 33 tests. Its argument list still announced itself as “v2 constructor order” in a comment nobody had re-read for two covenant versions. It is repaired and green now — five separate defects, four of them loud.

The fifth was not loud, and it is the one worth knowing. The successor's accounting was being written into three constructor slots that v4 had repurposed, so every successor compiled successfully at a wrong geometry, its address never matched the continuation the covenant derives, and everything was refused. The three baselines failed and every refusal passed — for reasons unrelated to the rule each was testing. A suite reporting 18 of 33 green would have looked healthy to anyone who did not read which 18.

Nothing was ever wrong on chain; every live grant was built by the tool the probes check. What decayed was the evidence, silently. That is the failure this page exists to talk about, so it stays written down: a figure nobody re-derives is a figure that decays, and a test suite nobody runs is the same thing wearing a green badge. Both suites now run on every push and weekly, which is the actual fix — the decay happened across two covenant versions with nobody touching the covenant in between.

Layer 2, in detail

Why most negative tests prove nothing.

The script engine collapses every failed require into one opaque error. It never says which rule rejected the transaction. So a test that asserts this was refused, against a transaction that never passed in the first place, proves only that something somewhere was wrong — a malformed signature script included. It would pass just as happily against a covenant with no rules in it at all.

The suite closes that gap with flip tests. One transaction is established that the engine returns Ok(()) for. Every other test starts from that exact transaction and changes one field. Because the baseline is accepted, a rejection can only have been caused by the field that changed.

The probe suite is built that way: fourteen transactions, each refusal paired with the nearest thing that must succeed. Run against the deployed covenant, every one behaves as required.

Probe — against the deployed covenantRequiredEngine
spend an epoch's allowance twicerefuserefused
rewind to an epoch already consumedrefuserefused
move forward to a fresh epochacceptaccepted
spend within the same epoch's headroomacceptaccepted
spend after expiryrefuserefused
delegate a narrowed childacceptaccepted
narrowed child pays inside its setacceptaccepted
narrowed child pays outside its setrefuserefused
reabsorb a child honestlyacceptaccepted
settle a child to the wrong partyrefuserefused
reabsorb a child with live children of its ownrefuserefused
reabsorb out of orderrefuserefused
revoke honestlyacceptaccepted
revoke, burning the balancerefuserefused

The second suite applies the same idea more narrowly, flipping one field at a time from a single accepted baseline — and the baseline is the load-bearing part, which is why its failure invalidated everything while looking like a partial pass.

From the accepted baseline, change one thingEngine
nothing — the baseline itselfaccepted
spend more than the per-spend caprejected
pay someone absent from the allowlistrejected
name one payee in the proof, send the money elsewhererejected
spend the money, do not record it in the successorrejected
move the reserve independently of the spendrejected
agent raises its own per-spend caprejected
agent widens its own allowlistrejected
agent extends its own expiryrejected
agent inflates its own budgetrejected
child delegated a wider allowlist than its parentrejected
child outliving its parentrejected
child born already spentrejected
delegation depth extendedrejected
delegate without reserving, under-reserving, over-reservingrejected

The accepted baseline is the load-bearing part. A suite made only of refusals would be passed by a covenant that refuses everything, which is useless and secure.

Both tables are current, against the deployed covenant.

The part a sales sheet leaves out

Six vulnerabilities, none found by reading the code.

Every one was found by building an attack and handing it to the real script engine. That is worth knowing about the ones not yet found: review has a poor record here, so the absence of a finding from reading this covenant is weak evidence about it.

The first three share a shape — the covenant checked what something was, and not how much of it there was. The fifth is the first one again, verbatim, in a code path written after it had been fixed elsewhere: a value check with no destination check. A fix is not a lesson until it is applied to the code written next.

All six are written up in full — mechanism, fingerprint of the affected version, and the fix — in GUARANTEES.md, alongside a section on what the chain cannot enforce.

Limits, not omissions

Things the covenant does not do, and will not.

Each of these is structural rather than a feature waiting to be built, and each is the kind of thing a reader would otherwise assume the other way round.

What none of this is

It is not an audit.

No third party has reviewed this covenant. Warda has run on testnet-10 only and has never held real money. A quoted price for external review exists and has not been paid. Nothing on this page should be read as a substitute for that, and the evidence above is offered because it is the most that can be assembled without it — not because it is equivalent.

One distinction worth keeping, because it is where this kind of claim usually goes wrong: a proof about a model of a covenant is not a proof about the covenant that gets deployed. Layer 2 above exists precisely to avoid that gap — it runs the compiled artifact, not a description of it. If layer 4 is ever added, it will be stated as what it is: a proof about a model, sitting beside a proof about the bytecode, which are different claims.

Don't take our word for it

Run the evidence yourself.

The bytecode layer, in the node's own engine. First build fetches rusty-kaspa and takes a few minutes; after that it is sub-second.

git clone https://github.com/ArtyKOMarkets/warda
cd warda/covenant/harness
cargo test --test spend -- --nocapture

The adversarial probes — transactions the covenant must refuse, each beside its nearest legitimate twin, because a covenant that refuses everything would pass a suite made only of refusals. Two of them are assembled without the SDK, because an attacker does not use our SDK.

cd warda/sdk && node --experimental-strip-types tools/probes.ts

And the grant behind every claim on this site is live. Its agent's private key is published in full.