Build 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
≈ 60 minutes
Attempts so far
#
Attempt
Amount
Sent to
Result
Vault state
What the covenant enforces
Who can signA withdrawal needs the owner's signature. Recovery needs the recovery key instead. No other key works.
What can leaveA withdrawal moves at most 500 KAS, and only to the vault's fixed payout address. Recovery empties the vault instead, and only to the fixed recovery address.
What comes nextEvery withdrawal must recreate the vault with the same KIP-20 covenant ID and the updated balance and last-spend score. A transaction that doesn't is spending a lookalike, not the vault.
How soon againA withdrawal must wait 36,000 DAA score since the vault's last spend (roughly one hour at 10 blocks per second) before it is valid again.
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
}
}
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 Scriptnot 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
Repository
Created
Last push
Stars
Releases
argent
2026-06-16
2026-08-31
22
None
argent-playground
2026-07-09
2026-08-31
7
None
argent-template
2026-07-24
2026-08-31
8
None
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 state
What Argent has for it
One coin per thing
Every 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 writers
Nothing 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.
# 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
# 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
Reusing test keys for mainnet is how funds get lost.
Use the right node
Testnet work needs a testnet build; mainnet builders run the released mainnet node.
Check sync and UTXO index
An unsynced node returns a balance that looks real and isn't.
Separate UI policy from consensus
A wallet warning isn't an enforced covenant.
Pin the submit surface
REST, 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 submit
Local 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 spend
Witness order, signature preimage, and redeem-script shape are easier to debug against a sibling transaction that already worked.
Label failed attempts narrowly
A 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.