Leash is an open rail for agents that spend on the open internet. The pattern we recommend for production is “client funds the agent, the agent makes them money”: funds physically live on the agent treasury (a Metaplex Core Asset Signer PDA owned by the agent), and the executive (a Privy embedded wallet, a Node keypair, a TEE — anything that can sign Solana transactions) is granted a capped delegation to move them.
Why a delegation (not a per-call wrap)?
Every production Solana x402 facilitator (devnet-facilitator.leash.market,
facilitator.leash.market, devnet-facilitator.leash.market, Coinbase CDP, and others) only
settles vanilla SPL TransferChecked transactions. Wrapping the transfer in
mpl-core::Execute would require facilitator-side changes the seller doesn’t
control. Instead Leash uses a one-time SPL.Approve wrapped in
mpl-core::Execute:
- The agent’s Asset Signer PDA is the on-chain owner of one ATA per stable mint (USDC, USDT, USDG).
- The executive (your wallet) becomes the SPL delegate of each
approved ATA, authorised to move up to
delegated_amountunits of that mint. Allowances are per-mint — approving USDC does not approve USDG. - Every settled call emits a vanilla
TransferCheckedsigned by the executive acting as a delegate of the mint the seller demanded. The SPL token program automatically debits the treasury and reduces the remaining allowance.
In the playground
- Mint an agent (see Create an agent; in the
web playground use Agents → New). The “Spend
allowance” field on tab 2 sets the initial delegation (defaults to $5
USDC). On submit, the playground:
- Mints the Core asset.
- Provisions the treasury’s curated stable ATAs (USDC on devnet;
USDC + USDT on mainnet) via
provisionTreasuryAtas. Your wallet pays rent (~0.002 SOL per ATA) once. From then on wallets, faucets, and other agents can transfer to the agent’s treasury without “recipient has no token account” errors. - Wraps an
SPL.Approveinmpl-core::Executeand signs once.
- Top up: send USDC on devnet (mint
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU, faucet.circle.com) directly to the treasury PDA. Because the ATA is already provisioned, the deposit lands in one tx without any extra setup from the sender. - Adjust or revoke at any time on the agent profile
(
/agents/<mint>→ “Spend allowance” card). Re-approving overwrites the cap; revoking clears it (delegate = None,delegated_amount = 0). - Re-provision a legacy agent (minted before auto-provisioning shipped) by clicking Provision stable ATAs on the treasury card — same helper, idempotent.
/buyer) reads delegation status before each call and
disables Fire request when the quoted price exceeds the remaining
allowance or the treasury balance.
Programmatically
Use@leash/registry-utils from any Node program:
Pre-provisioning ATAs
If you want every supported stable ATA created up front (so wallets and faucets can target the treasury without “recipient has no token account” failures), callprovisionTreasuryAtas
once after minting:
sourceTokenAccount returned by setSpendDelegation is what the buyer
must pass when constructing the client:
sourceTokenAccount is omitted the buyer-kit falls back to spending
from the executive’s own ATA for the quoted mint. Useful for headless smoke
tests; not what you want in production. To pay in a non-USDC stable, run
setSpendDelegation once per mint and pass the matching sourceTokenAccount
plus preferredCurrency ('USDC' | 'USDT' | 'USDG') on createBuyer.
On-chain shape
AftersetSpendDelegation lands you can read the SPL token account directly:
Delegated by the call price. When it hits
zero the next call returns 402 and the buyer-kit’s pre-flight reclassifies
the failure as insufficient_balance or insufficient_allowance (depending
on which floor was hit) on the ReceiptV1 so your cockpit can surface “top
up needed” or “raise allowance” without guessing.
See also
@leash/registry-utils→ — full delegation API.@leash/buyer-kit→ — wiringsourceTokenAccount.- x402 on Solana → — why a delegate transfer remains wire-compatible with every facilitator.

