Install
Three packages. Nothing to compile.
Compiled JavaScript with TypeScript declarations. Node 20+, no build step, no Rust toolchain and no Silverscript compiler — address derivation splices values into a compiled covenant template that ships inside the package.
# the SDK — build, sign and verify grants
npm install @warda_protocol/kaspa
# the MCP server — hand an agent framework a Warda tool
npx @warda_protocol/mcp
# the x402 adapter — pay metered APIs from a grant
npm install @warda_protocol/x402
Create a grant
import { buildGenesis, attachGenesisSignature, signDigest, RecipientSet } from "@warda_protocol/kaspa";
import template from "@warda_protocol/kaspa/covenant-template.json" with { type: "json" };
// Who this agent may pay, committed as a Merkle root.
const payees = new RecipientSet([vendorA, vendorB]);
const grant = {
authority: { principalKey, revocationKey },
state: {
agentKey,
budgetTotal: 1_000_00000000n, // 1000 KAS, ever
maxPerSpend: 200_00000000n, // 200 KAS per payment
epochLimit: 500_00000000n, // 500 KAS ...
epochLength: 1000n, // ... per 1000 blocks
recipientsRoot: payees.rootHex,
notBefore, expiresAt,
delegationDepth: 2n,
/* accounting starts at zero */
},
};
const unsigned = buildGenesis({ template, grant, funding, changeScriptPublicKey, fee });
const tx = attachGenesisSignature(unsigned, signDigest(unsigned.sighash, principalSecret));
Paying a metered API
import { WardaPayer, wardaFetch } from "@warda_protocol/x402";
const payer = new WardaPayer({
grant: { template, authority, state, recipients },
node, sign: agentSecret, // or a remote signer function
});
// A paid endpoint, called like a free one. The 402, the covenant
// spend and the X-PAYMENT proof are handled underneath.
const res = await wardaFetch("https://vendor.example/compute", {
method: "POST",
body: JSON.stringify({ prompt: "explain GHOSTDAG" }),
}, { payer });
It never pays twice. A second 402 means the payment is still settling, so the same proof is re-presented rather than a new one bought — and if the server never settles, it says the money is spent instead of paying again.
The agent spends, unattended
import { signSpend } from "@warda_protocol/kaspa";
const { tx } = signSpend({
template, authority, state,
utxo, // the grant's current coin
amount: 50_00000000n,
recipient: vendorA,
proof: payees.proof(vendorA), // proves the payee is allowed
claimedDaa, fee, computeBudget: 16,
}, agentSecret); // the principal is not involved
The SDK deliberately does not decide whether a spend is allowed — the covenant does, on chain, and it is the only thing that can. If the SDK reimplemented the rules, a divergence would fail by wrongly permitting: it says yes, and a budget drains on a spend nobody authorised. Reimplementing assembly fails the other way — a mistake produces a transaction the network rejects, which is loud.