@leash/buyer-kit is the smallest possible “scriptable buyer”. You give it a Core asset mint (the agent), a RulesV1 policy, and a @solana/kit signer. It returns a fetch that:
  1. Runs the policy gate.
  2. Pays via real x402 + the configured facilitator on a 402.
  3. Emits a chained ReceiptV1.
import { createBuyer } from '@leash/buyer-kit';
import { createKeyPairSignerFromBytes } from '@solana/kit';
import fs from 'node:fs';

const signer = await createKeyPairSignerFromBytes(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.LEASH_BUYER_SECRET_KEY!, 'utf8'))),
);

const buyer = createBuyer({
  agent: process.env.AGENT_ASSET!,
  rules: {
    v: '0.1',
    budget: { daily: '1.0', perCall: '0.01', currency: 'USDC' },
    hosts: { allow: ['seller-demo.local'] },
    triggers: [],
  },
  signer,
  networks: ['solana-devnet'],
  rpcUrl: 'https://api.devnet.solana.com',

  // Recommended: spend from the agent treasury, not the executive's wallet.
  // Pass the treasury **ATA for the mint you intend to pay with** (same mint
  // as `preferredCurrency`). When omitted, buyer-kit can still derive the ATA
  // from `agent` + quoted `asset` on failures, but passing it explicitly avoids
  // ambiguity. See [Fund an agent](./fund-an-agent).
  sourceTokenAccount: process.env.LEASH_BUYER_SOURCE_TOKEN_ACCOUNT,

  // When the seller lists multiple stables in `accepts[]`, pick one.
  preferredCurrency: 'USDC',

  onReceipt: (r) =>
    fetch(`http://localhost:8787/a/${r.agent}/receipts`, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify(r),
    }),
});

const { response, receipt } = await buyer.fetch('https://seller-demo.local/tag', {
  method: 'POST',
});

console.log(receipt.tx_sig); // real Solana signature
In agent-funded mode the executive needs a tiny amount of devnet SOL for the network fee (the facilitator pays the rest), the agent treasury holds the stablecoin being moved (USDC / USDT / USDG depending on preferredCurrency and what the seller accepts), and the executive must already be the SPL delegate of that mint’s treasury ATA — see Fund an agent. In legacy mode (no sourceTokenAccount) the signer wallet itself needs SOL plus the token being paid. See Real x402 on Solana for the wire-level header walkthrough and the facilitator landscape.

Browser usage

In a browser, supply any @solana/kit TransactionPartialSigner. The web playground (apps/web/lib/privy-svm-signer.ts) adapts a Privy embedded wallet to this interface — copy that file as a starting point.

Hosted: the autonomous-agent cockpit

The web playground at apps/web/buyer ships this exact flow as a UI:
  1. The user signs in with Privy (email / Google).
  2. The user picks one of their agents from the dropdown.
  3. The Privy wallet acts as the agent’s registered Executive — per Metaplex’s Run an Agent docs — and signs every x402 SPL transfer on the agent’s behalf.
  4. Behaviour rules captured at agent creation are fed straight into createBuyer({ rules }), so the policy gate enforces them before every call.
  5. Pay with selects the settlement stable; treasury balance and allowance panels re-read that mint’s ATA. If discovery shows the link does not accept the chosen currency, Fire request stays disabled.
No private keys ever leave the browser, and the same BuyerKit API backs both the headless CLI and the browser cockpit.