Before you start
One prerequisite, and there is now a way round it.
You need to read the UTXO set. You do not need to run a node.
Everything in this SDK builds and signs transactions locally — no server sees a
key — but building one means reading live chain state, and that has to come from
somewhere.
# the public resolvers, over the encoding they serve
npm i @warda_protocol/borsh @kluster/kaspa-wasm
warda node --borsh
This page used to say the resolver path found you nothing, and that running a node was
not one option of two but the option. Here is what was actually going on, since
it is the more useful fact.
There are sixteen public resolvers — named hosts under kaspa.stream,
.red, .green and .blue, listed in rusty-kaspa's
Resolvers.toml. They serve borsh. Measured 10 September
2026: 404 for /wrpc/json, 200 for
/wrpc/borsh, mainnet and testnet both. The nodes were never missing; we
were asking in a language they do not answer in.
And reading over borsh was not enough to spend. Borsh is positional: no field
names on the wire, so a struct the encoder does not know about is not an unknown field
but an absent one, and absence shifts everything after it. The published WASM
SDK predates covenants, so a grant spend through it would have been well-formed,
correctly signed, and bound to nothing. rusty-kaspa's own bindings have
carried covenants since Toccata and builds from those revisions are published —
so the barrier turned out to be packaging, not protocol.
Nor does that mean trusting a stranger's binary. The covenant binding is
part of the sighash, so any difference between what was signed and what the encoder
produced changes the transaction id. Every submit computes it both ways and refuses to
broadcast unless they match byte for byte. borsh/tools/agree.ts runs the
same check against whatever build you installed, before you fund anything.
To rely on it rather than try it, run your own — a node you do not run is a node
you are trusting:
BASH
kaspad --testnet --netsuffix=10 --utxoindex \
--rpclisten-json=127.0.0.1:18210
Both flags matter. Without --utxoindex a node reports every grant as spent.
The JSON wRPC port is separate from the Borsh one (18210 versus 17210) and only listens
when that flag is given — note the =, which the flag requires. The SDK
compiles no resolver host in and no node URL in, because naming one is a decision about
who you trust to answer "which node", and that belongs to you.
There is also a floor under how small a payment can be. Kaspa charges storage
mass for the small outputs a transaction creates, and a payment of 0.01 KAS is refused by
consensus — massing 1,000,000 against a ceiling of 500,000 — with the grant,
the budget and the fee all perfectly fine. In practice nothing under about 0.02 KAS can be
broadcast, so per-request micropayments below that are not a thing this chain does.
tools/check-node.ts interrogates whichever node you point at before you build
anything against it. Each thing it checks fails by returning a plausible wrong answer that
reads as a problem with your grant rather than with the node.