Build on Kaspa

Build Kaspa apps

Spend limits, escrow, and proof checks run in Kaspa's own script, live since Toccata activated at DAA score 474,165,565.

Where to check a covenant claim: how they work, their status, and the risks

See how covenants work. Claims: Status. Risk: Risks.

Route

Pick the route

Most products need only payments and wallet UX; Toccata matters once a spend rule must be readable first.

What the app needsRouteWhy that one
Payment, receipt, balance, withdrawal, or support evidenceLive network workCurrent wallets, APIs, and accepted-transaction checks already handle this.
Vault, escrow, spend cap, asset rule, release, refund, or timeoutToccata covenantsA bounded spend rule is simpler to build and audit than a full app runtime. Split into one covenant per state if the states never touch.
One app keeps its own richer state from accepted Kaspa transactionsBased app or replay pathThe app proves or replays its own state without needing cross-app atomicity.
Each action needs its own proof, privacy check, or custom validity ruleInline ZKIt fits, and costs more proof and ops work than covenants or based apps do.
The claim depends on another chain, an oracle, a price, or a real-world eventInline ZK, plus an anchorA proof checks its own inputs, never the world. Name the anchor first: source root, finality certificate, oracle, or challenge process.
Many users mutate the same app state at onceFuture full vProgsBuild the narrow app first. Argent's Inter-Covenant CommunicationSeparately compiled covenant apps joined into one all-or-nothing transaction. already runs separate covenant apps as one, in unaudited offline demos.

Fungible tokens build on KCC-0020, a draft standard with a known supply-split defect: see KIPs and KCCs.

A covenant can only inspect its own spending transaction; no KIP gives it a shared-state read.

Demo

Attack a covenant vault

A worked example, not a real chain.

This vault holds 10,000 KAS behind four rules. Get money out without satisfying all four.

Status: enforcement is live on mainnet; the rule's own notation is illustrative.

Get money out

Start here, or try your own.

Direct attempts, each breaks one rule on purpose

Break no rule at all

How you're trying to get it out

≈ 60 minutes

Rule definitions and sources
state VaultState {
    byte[32] owner_pk_hash;
    byte[32] recovery_pk_hash;
    byte[32] payout_spk_hash;
    byte[32] recovery_spk_hash;
    int balance;
    int last_spend_daa;
}

actor Vault owns VaultState {

    entry withdraw(int amount, sig owner_sig, pubkey owner_pk)
        emits next: Vault
    {
        require(blake2b(owner_pk) == owner_pk_hash);
        require(checkSig(owner_sig, owner_pk));

        // what can leave: fixed cap, fixed destination
        require(amount <= 500 && amount <= balance);
        require(tx.outputSpkHash(0) == payout_spk_hash);
        require(tx.outputAmount(0) == amount);

        // how soon again: time since this covenant's own last spend
        require(tx.inputDaaScore(self) - last_spend_daa >= 36000);

        // what comes next: same KIP-20 covenant id, updated state
        VaultState new_state = {
            owner_pk_hash: owner_pk_hash,
            recovery_pk_hash: recovery_pk_hash,
            payout_spk_hash: payout_spk_hash,
            recovery_spk_hash: recovery_spk_hash,
            balance: balance - amount,
            last_spend_daa: tx.inputDaaScore(self),
        };
        become next <- Vault(new_state);
    }

    entry recover(sig recovery_sig, pubkey recovery_pk) {
        require(blake2b(recovery_pk) == recovery_pk_hash);
        require(checkSig(recovery_sig, recovery_pk));

        // no cap, no delay: recovery ignores both
        // but the destination is still fixed, and the vault ends
        require(tx.outputSpkHash(0) == recovery_spk_hash);
        require(tx.outputAmount(0) == balance);
        // no `become`: this covenant's lineage ends here
    }
}

Pseudocode uses Argent's research-stage syntax; see Argent and Silverscript.

Opcodes match KIP-17: OpTxOutputAmount, OpTxOutputSpk, OpTxInputDaaScore, OpTxLockTime; both it and KIP-20 are live.

A spend must carry the same covenant_id as its UTXO, or prove a fresh one via genesis hash.

Evidence

How much of this is actually running

An indexer counts 84,196 covenants on mainnet, 687 active, holding about 1,561,431 KAS; Testnet-10 carries far more, about 88,493 active.

Use caseWhat the rule controlsStatus
VaultWithdrawal delay, recovery key, spend cap, or escape path.Live
EscrowRelease, refund, timeout, or dispute transition.Live
Assurance fundingPayout only after enough matching commitments arrive before a deadline.Live
Controlled assetsTransfer rules, controller input, or issuer rule.Live
ZK proof checksA claim about chosen public inputs.Live
Cross-app compositionSeveral app states succeed or fail together in one transaction.Demos only
Shared mutable stateMany users mutating one app state at once.Roadmap

Baseline written into this page September 1, 2026.

Sources, all read Status: Active: KIP-16, KIP-17, KIP-20, KIP-21.

Application layer

Argent and Silverscript

Toccata made covenants enforceable. Argent, Sutton's compiler, turns a readable application into the Silverscript the network already checks. Silverscript 1.0 shipped on 9 September 2026; Argent remains pre-release and needs further audit and hardening for general production use.

Try it

One line at a time, from Argent down to Kaspa Script

Nothing reaches the network as Argent: every line ends up as script a node already checks. The Ticket example hands off a ticket in three lines.

Argent · .ag
Silverscript · .sil

Kaspa Script not published

Only the bottom row runs; it decides whether the money moves. require, blake2b, and checkSig rows are exact builtins; become is illustrative, since neither repo publishes Argent's real generated .sil. Lines from argent-lang/argent and Silverscript.

The gap it fills

A covenant is opcodes. An application is several agreeing.

A covenant is a rule attached to a coin, raw opcodes: readable alone, hard to audit once several agree. Argent, Sutton's unofficial compiler, takes an actor description and writes the script.

Inter-Covenant CommunicationSeparately compiled apps joined into one all-or-nothing transaction. lets one app's actor authorize another's, so two land together or not: second of three rungs on the What is Kaspa page, the first not live.

Open the language model and a worked example

No contract address to call, only outputs. A state is a typed record, an actor owns one, an entry spends it, and become names the successor.

state TicketState {
    byte[32] owner;
    int units;
}

actor Ticket owns TicketState {
    entry transfer(byte[32] next_owner, sig owner_sig, pubkey owner_pk) emits next: Ticket {
        require(blake2b(owner_pk) == owner);
        require(checkSig(owner_sig, owner_pk));
        require(next.value == self.value);

        TicketState new_state = {
            owner: next_owner,
            units: units,
        };

        become next <- Ticket(new_state);
    }
}

app Tickets {
    actor Ticket;
}

A failed require invalidates the transaction: no revert, no gas refund, no program to interrupt. The compiler emits three inspectable outputs, plain .sil with no covenant macros, targeting KIP-20's covenant identities, the piece Toccata carried to mainnet.

Repository signals

Three active repos, zero releases

RepositoryCreatedLast pushStarsReleases
argent2026-06-162026-08-3122None
argent-playground2026-07-092026-08-317None
argent-template2026-07-242026-08-318None

Baseline read from GitHub on September 1, 2026.

What's proven, what isn't

Partitioned state works. Shared state is a design direction.

Shape of the stateWhat Argent has for it
One coin per thingEvery published example: one UTXOUnspent transaction output. Value sits in separate coins, spent whole and replaced by new ones, instead of living in one account balance that gets edited. per ticket, per game, per position.
One state, many writersNothing public. That shape is most of what people mean by DeFi.

Quoting the README's caution without its working-pieces list misdescribes it. Working compiler examples do not establish production readiness.

Open the evidence: the DEX example, commit history, and the README status

The playground's DEX is a single pair, no replicas or divergence in SECURITY.md. Sutton wrote 73 of the compiler's first 77 commits, reaching 85 by September 1, 2026.

Checked 10 September 2026: Silverscript v1.0.0 shipped on 9 September, an official stable release following review, testing, and standardization. Argent's README still says it is not release-ready and needs further audit and hardening before general production use. A stable foundation does not certify the compiler above it or an app built with either.

Sources, with what each backs

Demo

What a proof can and can't verify

KIP-16's proof check confirms validity, not where the inputs came from.

Ask a centralized server

Check the proof yourself

Why a proof can't verify outside facts

OpZkPrecompile (KIP-16) checks a proof against a key and inputs: math confirmed, not where inputs came from.

An outside fact needs an oracle to sign it; the proof checks only the signature. Kaspa's L1 has none.

Sources: KIP-16, docs.kaspa.org/programmability. No proof runs here; this only names what would and wouldn't pass.

What to build

Products, sorted by what they need

Needs only the live network, already has usersWhat it does
Receipts and checkoutInvoices, tips, and withdrawals showing inclusion, confidence, and risk as separate facts.
Self-custody toolsAddress books, payment requests, and accounting exports for keys people hold themselves.
Support helpersSplitting exchange delay from network delay, the thing most tickets mix up.
Node and API monitorsDiff a public API against your own node; flag a stale provider first.
Proof pagesOne page carrying user action, txid, source, status label, and current blocker.
Activity dashboardsPayments, mining, and spam pulled apart, so one raw count isn't a signal.

A synced node and Silverscript compilation come first; no getUtxosByCovenantId RPC exists yet.

Needs a spend ruleWhat it does
Atomic market primitivesSwaps, auctions, and OTC flows, with script behavior staying bounded.
Games and state machinesTurn-based apps where player state, move routing, and settlement are separate steps.

Fact-check a claim: claim fact-check. Toccata prep: status operator checklist.

Verify yourself

Run the checks yourself

Climb the ladder: an explorer read, a second API, then a node reading chain state.

Setup: node, testnet, or API

Run a Rusty Kaspa node

# Docker route; Rust+Cargo for source. A node verifies chain state, not wallet keys.
docker pull kaspanet/rusty-kaspad:latest
mkdir -p ~/kaspa-data
docker run -d --name kaspa-node --restart unless-stopped \
  -v ~/kaspa-data:/app/data \
  -p 16110:16110 -p 16111:16111 -p 17110:17110 -p 18110:18110 \
  kaspanet/rusty-kaspad:latest

docker ps
docker logs --tail=80 kaspa-node
# Source build with UTXO indexing (needed for wallet/UTXO queries). Wait for full sync.
git clone https://github.com/kaspanet/rusty-kaspa
cd rusty-kaspa
cargo run --release --bin kaspad -- --utxoindex

# Terminal wallet and RPC, from a checkout. Read address, amount, fee, network before signing.
cd cli
cargo run --release

# wRPC is off by default; bind carefully on public machines.
cargo run --release --bin kaspad -- --utxoindex --rpclisten-json=default

TN12 lab practice

# Check netsuffix/release/flags against active docs first.
cargo run --release --bin kaspad -- --testnet

cargo run --release --bin kaspad -- --testnet --netsuffix=10 --utxoindex

TN10: tn10-toc2, tn10-toc3. TN12 lab: repo, results, playground.

# Fund only printed kaspatest: addresses from a TN12 faucet; every Kaspa testnet
# shares that prefix, so TN10 faucet tKAS won't work here. --submit broadcasts for real.
git clone https://github.com/parker2017code/tn12-covenant-vault-demo
cd tn12-covenant-vault-demo
npm ci
npm run check:all
npm run check:tn12

Hosted API checks

Kaspa Developer Platform: hosted API for chain data and tx checks, not a protocol source.

POST

Transaction acceptance

# Batch-check txids for acceptance, block hash, confirmation count.
curl --request POST \
  --url https://api.kas.fyi/v1/transactions/acceptance \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <api-key>' \
  --data '{"transactionIds":["<txid>"]}'
GET

DAA-score blocks

# Bounded range; KDP caps each request at 100 scores.
curl --request GET \
  --url https://api.kas.fyi/v1/blocks/daa-score/{daa_score_start}/{daa_score_end} \
  --header 'x-api-key: <api-key>'
GET

Address history

# Large histories need cursor pagination; cache results.
curl --request GET \
  --url 'https://api.kas.fyi/v1/addresses/<address>/transactions?limit=100' \
  --header 'x-api-key: <api-key>'
DATA

API data rules

# Amounts are sompi strings. Key goes in x-api-key. 429 means back off.
1 KAS = 100000000 SOMPI
amount: "31112708372"

api-tn10.kaspa.org's REST API drops compute_budget (see openapi.json): fails with "script units exceeded." Use a kaspad node.

KDP: index, llms.txt. Also: mainnet REST API, docs.kaspa.org references.

Checks that catch the expensive mistakes

Builder habitWhy, and what to check
Pick network firstReusing test keys for mainnet is how funds get lost.
Use the right nodeTestnet work needs a testnet build; mainnet builders run the released mainnet node.
Check sync and UTXO indexAn unsynced node returns a balance that looks real and isn't.
Separate UI policy from consensusA wallet warning isn't an enforced covenant.
Pin the submit surfaceREST, JSON wRPC, Borsh wRPC, and SDK versions can hand back different transaction shapes. Log SDK, node, network, endpoint, encoding, and tx version every time.
Fetch accepted state after submitLocal construction proves a transaction was built, not that the network accepted it. Record is_accepted, accepting block, output type, address, amount.
Compare with a known working spendWitness order, signature preimage, and redeem-script shape are easier to debug against a sibling transaction that already worked.
Label failed attempts narrowlyA tooling failure and a consensus failure look identical from outside. Mark it bad config, stale tooling, submit mismatch, or confirmed rejection.

Stay testnet-only until mainnet evidence says otherwise.

References

What to read before writing code

Docs, SDKs, repos, infrastructure
  1. Kaspa docs: Getting started, Programmability, Transaction payload, Kaspa node.
  2. JavaScript SDK: RpcClient docs, signTransaction docs, WASM examples.
  3. Michael Sutton: vProgs masterclass, vprogs repo, covenant++ gist, based-ZK-rollup notes.
  4. Infrastructure: rusty-kaspad Docker, simply-kaspa-indexer, kaspa-js, DeepWiki, Core R&D Telegram.