@leash/buyer-kit wraps @leash/core into the smallest possible “scriptable
buyer”. One call, one chained receipt, one real x402 SPL transfer.
What happens on each call
- Policy gate (
evaluate) — host allow-list, per-call/daily ceilings, pause check. Adenyshort-circuits and emits adenyreceipt; no network traffic, no spend. paidFetch=wrapFetchWithPaymentfrom@x402/fetch, registered withLeashDelegateExactSvmScheme(whensourceTokenAccountis set) orLeashExactSvmScheme(owner mode) from@leash/core. Both use theexactscheme id and are wire-compatible with every x402 facilitator. The first request is normal; on402 Payment Requiredthe wrapper:- Decodes
PAYMENT-REQUIREDto the seller’saccepts[]. - Picks the entry matching
preferredCurrency(by mint address). Throwspreferred_asset_unavailableif the seller doesn’t offer it. - Builds the Solana transaction:
[setLimit, setPrice, sellerAtaCreate, feeVaultAtaCreate?, TransferChecked(seller), TransferChecked(fee)?, memo]. The two idempotent ATA-create instructions are always included so first payments on a fresh mint (e.g. USDG on devnet) succeed without manual setup — the facilitator pays the ATA rent. - Partially signs as the buyer delegate, then replays the request with
PAYMENT-SIGNATURE.
- Decodes
- Receipt finalization (
finalizeReceipt) — hashes the request, pullstx_sig+payment_requirements_hashfrom the seller’sPAYMENT-RESPONSEheader, chains againstprev_receipt_hash, and returns a fully-formedReceiptV1stamped with the settled mint (USDC, USDG, etc.). On a failed 402 the receipt’spricereflects the mint the buyer attempted to pay with, not the seller’s primary.
Browser usage (Privy / wallet-adapter)
Provide anyTransactionPartialSigner from @solana/kit. The web playground
adapts a Privy embedded wallet via apps/web/lib/privy-svm-signer.ts —
copy that file as a starting point for your own dApp.
Receipts by default
You no longer need to passonReceipt — when LEASH_API_URL /
LEASH_API_KEY (or LEASH_RUNNER_URL) are set in the environment,
createBuyer resolves a fan-out sink that POSTs every receipt to
each destination. Pass onReceipt: false to disable forwarding
entirely, or receipts: { runnerUrl, apiUrl, apiKey, fetch } to
configure explicitly. See Receipts by default.
API
sourceTokenAccount is set the buyer-kit registers
LeashDelegateExactSvmScheme from @leash/core instead of the vanilla
@x402/svm exact scheme — wire-compatible with every facilitator (the
on-chain instruction is still a plain SPL TransferChecked), but with the
agent treasury as the source instead of the executive’s own ATA.
Automatic failure classification
When the seller returns402 without settling, the kit reads the on-chain
state of the source token account and stamps the receipt’s reason with one
of:
| Reason | Meaning | Fix |
|---|---|---|
preferred_asset_unavailable | You set preferredCurrency but the seller’s accepts[] omits that mint. | Pick a currency the link advertises, or ask the seller to add it. |
insufficient_balance | Treasury holds less than the seller demanded (for the quoted mint). | Top up the agent’s treasury ATA for that mint. |
insufficient_allowance | Delegate cap is below the demanded price. | Re-run setSpendDelegation with a higher cap for that mint. |
no_delegate | The treasury ATA exists but no SPL delegate is set. | Run setSpendDelegation from @leash/registry-utils. |
wrong_delegate | The set delegate is not the executive that signed. | Re-approve with the current executive. |
ata_missing | The agent has never held this mint. | Send any amount of the mint to mint the ATA. |
<classified>: <seller breadcrumb>
(e.g. insufficient_balance: transaction_simulation). UI panels match on the
prefix; the suffix is purely diagnostic.
The source ATA is resolved in this order:
cfg.sourceTokenAccountif you pass it (fastest path).- Derived from
cfg.agent + quote.price.assetviaderiveAgentTreasuryAta— kit-native PDA derivation that mirrorsmpl-core’sfindAssetSignerPda. Brand-new agents that have never been delegated still get the precise diagnostic.
cfg.rpcUrl to be set so the kit can issue a single
getAccountInfo against the source ATA.
See Real x402 on Solana for the protocol-level
walkthrough and Fund an agent for the wiring
steps.
HTTP equivalent (polyglot SDKs)
The fullcreateBuyer flow — quote, policy gate, prepare, execute,
finalize, verify — is exposed as eight stateless REST endpoints under
/v1/buyer/*. You stay in control of the signer; the API handles the
seller proxy, receipt finalization, and watchlist enrollment. See
Buyer endpoints for the complete surface, and
POST /v1/buyer/payment/execute
in particular for the convenience path that’s the closest equivalent
to buyer.fetch(...).
