Create / register

  • createAgent(umi, input, config?) — one-shot mint: calls Metaplex’s mintAndSubmitAgent. Creates a Core asset and registers its Agent Identity in a single transaction. By default the agent’s on-chain metadata advertises a services.receipts URL of https://api.leash.market/v1/receipts/{agent} so explorers and indexers can find the feed automatically — pass receiptsUrl: false to omit, receiptsUrl: 'https://...' to override, or set LEASH_NO_RECEIPTS_URL=1 / LEASH_RECEIPTS_URL=... to control it from the environment. See the Create an agent guide and Receipts by default.
  • prepareAgentMint(umi, input, config?) / sendPreparedAgentMint(umi, prepared) — two-step variant for priority fees, hardware wallets, custom retries. Honours the same receiptsUrl rules.
  • registerAgentIdentity(umi, { asset, collection, agentRegistrationUri }) — BYO flow: attaches an Agent Identity plugin to a Core asset you already minted.

Read

  • getAgentIdentityStatus(umi, asset) — returns { registered, treasury, account? }. Uses safeFetchAgentIdentityV1FromSeeds so unregistered assets return registered: false instead of throwing.
  • getAgentTreasury(context, asset) — derives the Asset Signer PDA (the agent’s built-in wallet).
  • loadAgentRegistration(uri) / loadRegistrationDocument(uri) — fetch + Zod-validate RegistrationV1.

Spend allowance (treasury → executive)

The agent treasury (an Asset Signer PDA) physically owns the funds. To let an executive wallet move them via x402, approve a capped SPL delegation on the agent’s ATA for each stable mint you want to spend (USDC, USDT, USDG). Implemented as an SPL.Approve wrapped in mpl-core::Execute so the PDA can authorise it via CPI; the resulting delegation is wire-compatible with every existing x402 facilitator (the on-chain transfer is plain TransferChecked). Allowances are per-mint — re-run setSpendDelegation once per stable you intend to settle in.
  • setSpendDelegation(umi, { agentAsset, mint, executive, amount, payer?, authority?, tokenProgram? }) — defensively inspects the agent’s ATA and approves executive for up to amount atomic units. If the ATA already exists with the correct mint + owner the create step is skipped (single Execute(Approve) tx); if the ATA is absent it’s created via CreateIdempotent; if it exists with a mismatched owner you get a precise error rather than the generic "Provided owner is not allowed". Returns { signature, treasury, sourceTokenAccount, delegatedAmount, delegate }.
  • revokeSpendDelegation(umi, { agentAsset, mint, payer?, authority?, tokenProgram? }) — drops delegate and delegated_amount to zero.
  • getSpendDelegation(umi, { agentAsset, mint, tokenProgram? }) — reads on-chain state without sending a tx. Returns { treasury, sourceTokenAccount, sourceExists, balance, delegate, delegatedAmount }.

provisionTreasuryAtas

  • provisionTreasuryAtas(umi, { agentAsset, network?, mints?, payer? }) — idempotently creates the agent treasury’s Associated Token Accounts for a curated set of stablecoins. Defaults to KNOWN_STABLES[network]: USDC on devnet; USDC + USDT on mainnet. Pass a custom mints array to override. Returns { treasury, atas: [{ mint, symbol, address, tokenProgram, created, signature? }] }created: false means the ATA was already valid (no tx broadcast). Re-run safely as many times as you like. Why pre-create? Two reasons: (1) wallets and faucets refuse to send to an address that doesn’t have an ATA, so provisioning ahead of time means “send USDC to this treasury” Just Works, and (2) setSpendDelegation then becomes a single Execute(Approve) instead of also having to create the ATA inline — fewer surfaces for "Provided owner is not allowed" to misfire on.
  • Constants: SPL_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, KNOWN_STABLES.
Full walkthrough in Fund an agent.

Execute (delegation)

Mirrors the Metaplex Run an Agent guide step-for-step. The web playground signs all three calls with the connected Privy embedded wallet — there is no server-side signer in the user flow.
  • registerExecutive(umi, { payer?, authority? }) — one-time registerExecutiveV1. Defaults payer = umi.payer (matches the docs snippet). Override authority when a different signer than umi.identity should own the executive profile (multisig, hardware wallet).
  • hasExecutiveProfile(umi, authority) — does an ExecutiveProfileV1 exist for that wallet?
  • delegateExecution(umi, { agentAsset, executiveAuthority, payer?, authority? })delegateExecutionV1. The authority (default umi.identity) must be the asset owner — the program rejects the tx otherwise.
  • verifyDelegation(umi, { agentAsset, executiveAuthority }){ delegateRecord, exists }. Derives the ExecutionDelegateRecordV1 PDA and runs the docs’ umi.rpc.getAccount(...).exists check.
  • isExecutionDelegated(umi, { agentAsset, executiveAuthority }) — boolean shortcut around verifyDelegation.

Withdraw

Owner-driven drain of treasury funds — both SPL stables and native SOL (Genesis creator fees). Both paths share one mpl-core::Execute wrap so the Asset Signer PDA can authorise the inner instruction via CPI; only the inner instruction differs (SPL TransferChecked vs SystemProgram.Transfer). Same wire-shape works for classic SPL Token, Token-2022, and the System program. Full guide: Withdraw treasury funds. SPL stables
  • withdrawTreasury(umi, { agentAsset, mint, destination, amount, payer?, authority?, tokenProgram? }) — moves amount atomic units to destination. Bundles a CreateIdempotent ATA in front when the destination has no ATA yet.
  • withdrawTreasuryAll(umi, { agentAsset, mint, destination, ... }) — reads the on-chain balance and drains it. Returns null (no tx broadcast) when the treasury is empty.
  • getTreasuryBalance(umi, { agentAsset, mint, tokenProgram? }) — read-only “what’s the max I can pull” helper.
Native SOL (creator fees from a Genesis token launch land here)
  • withdrawTreasurySol(umi, { agentAsset, destination, lamports, payer?, authority? }) — moves exactly lamports to destination via a hand-rolled SystemProgram.Transfer wrapped in mpl-core::Execute. No ATA needed.
  • withdrawTreasurySolAll(umi, { agentAsset, destination, reserveLamports?, ... }) — reads the PDA’s lamport balance, subtracts a configurable reserve (default 5_000 lamports), and transfers the rest. Returns null when the spendable amount is zero.
  • getTreasurySolBalance(umi, { agentAsset, reserveLamports? }){ treasury, lamports, sol, spendableLamports, spendableSol } for UI gating.
  • SYSTEM_PROGRAM_ID, DEFAULT_SOL_RESERVE_LAMPORTS — re-exported constants for advanced consumers.

Agent token (Metaplex Genesis)

Wraps the Genesis SDK so an agent can launch its canonical token in one call. Creator fees route to the agent’s Asset Signer PDA (the treasury), and the launch transactions are wrapped in mpl-core::Execute so the agent itself executes them on-chain. Full guide: Launch an agent token.
  • launchAgentToken(umi, { agentAsset, network?, setToken?, token, launch?, wallet?, ... }) — one-shot createAndRegisterLaunch. Returns { mintAddress, genesisAccount, signatures, launch, token, agentAsset, network, agentTokenSet } with base58-encoded signatures. setToken: true is permanent — once an agent’s token is bound it cannot be changed, replaced, or unset.
  • GENESIS_TOKEN_IMAGE_URL_PREFIX / isGenesisTokenImageUrl(url) — Metaplex Genesis token.image URL rule helpers (see Metaplex docs).
  • prepareAgentTokenLaunch(umi, input) / sendPreparedAgentTokenLaunch(umi, prepared, opts?) — two-step variant for hardware wallets, custom retries, or inspecting the unsigned bundle before sending.
  • setAgentToken(umi, { agentAsset, genesisAccount, collection?, payer?, authority? }) — wraps setAgentTokenV1 in mpl-core::Execute. Use this when you launched a Genesis token outside Leash and want to bind it to your agent later (the docs’ “Set Agent Token” snippet).
  • getAgentToken(umi, agentAsset){ agentAsset, treasury, hasToken, mint, source } where source is 'v1' | 'v2' | 'none'.
  • hasAgentToken(umi, agentAsset) — boolean shortcut.
Re-exports the Genesis types callers most often need: TokenMetadata, BondingCurveLaunchInput, GenesisApiConfig, SvmNetwork.

Registration resolution

  • resolveByoUri(uri) — fetches + validates RegistrationV1 from a public URL.
  • parseLeashBlock(input) — Zod parse helper for LeashBlockV1.

Operator keypair

Pure Ed25519 helpers for the agent’s own off-chain signing identity — independent of the owner / executive on-chain wallets. Bytes are Solana-compatible (secret(32) || public(32), same shape as solana-keygen), so the same secret can be loaded by any tool in the ecosystem. No Solana RPC dependency — runs in Node, the browser, and edge runtimes. See Identities for when to use this vs. an owner / executive wallet.
  • generateOperatorKeypair(){ publicKey, secretKey, pubkey } — fresh keypair from the platform’s CSPRNG.
  • operatorFromSeed(seed) — deterministic derivation from a 32-byte seed (tests, KMS, mnemonic-derived flows).
  • signWithOperator(secretKey, message) — Ed25519 signature over an arbitrary payload (typically a 32-byte digest).
  • exportOperatorJson(kp) / importOperatorJson(raw) — round-trip the secret as a solana-keygen-style JSON byte array.
  • operatorPublicId(kp){ pubkey, fingerprint } — public-only summary safe for logs and UI.
  • pubkeyToBytes(pubkey) — base58 → 32-byte Uint8Array (throws on invalid input).
  • operatorRegistration(pubkey)AgentRegistration entry (leash:operator / solana:<pubkey>) ready to drop into createAgent({ registrations: [...] }).
  • readOperatorRegistration(reg) — inverse: extracts the operator pubkey from an on-chain registration entry, or null if it isn’t one.

Prepare/Send split

Every mutating helper in this package ships in two flavours:
  1. One-shot (createAgent, setSpendDelegation, withdrawTreasury, …) — builds, signs with umi.identity, and submits. Returns the signature plus echo fields. Use this when the SDK runs in the same process as the signer.
  2. prepare* — builds an unsigned TransactionBuilder (or returns null when there’s nothing to do) plus the same echo fields. Pair with await prepared.builder.sendAndConfirm(umi) to sign and submit later. Use this when the signer lives elsewhere (hardware wallet, browser extension, remote RPC, the upcoming Leash HTTP API), when you need to add priority fees / custom retries, or when you want to inspect the tx before broadcasting.
The two-step prepareAgentMint + sendPreparedAgentMint and prepareAgentTokenLaunch + sendPreparedAgentTokenLaunch pairs are slightly different — they go through the Metaplex hosted Agent / Genesis APIs rather than building a Umi TransactionBuilder locally — so they keep their own send helpers. Everything else returns a builder you can finish off with .sendAndConfirm(umi) directly.
One-shotPrepare siblingNotes
createAgentprepareAgentMint + sendPreparedAgentMintGoes through Metaplex’s hosted mint API, not a local builder.
registerAgentIdentityprepareRegisterAgentIdentityBYO Core asset.
setSpendDelegationprepareSetSpendDelegationReturns willCreateAta so callers can size fees correctly.
revokeSpendDelegationprepareRevokeSpendDelegationPure builder — no RPC pre-flight.
provisionTreasuryAtasprepareProvisionTreasuryAtasReturns null when every requested ATA already exists.
registerExecutiveprepareRegisterExecutiveOne-time per executive wallet.
delegateExecutionprepareDelegateExecutionOwner-signed grant.
withdrawTreasuryprepareWithdrawTreasuryBundles a CreateIdempotent for the destination ATA when needed.
withdrawTreasuryAllprepareWithdrawTreasuryAllReturns null when the treasury ATA balance is zero.
withdrawTreasurySolprepareWithdrawTreasurySolPure builder — lamports is taken at face value.
withdrawTreasurySolAllprepareWithdrawTreasurySolAllReturns null when the spendable lamport amount (after reserveLamports) is zero.
setAgentTokenprepareSetAgentTokenBind a Genesis token to an existing agent.
launchAgentTokenprepareAgentTokenLaunch + sendPreparedAgentTokenLaunchGenesis multi-tx bundle.
Example — defer signing to a Ledger:
import { prepareSetSpendDelegation } from '@leash/registry-utils';

const prepared = await prepareSetSpendDelegation(umi, {
  agentAsset: 'CoreAss…Asset',
  mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU', // devnet USDC
  executive: executiveWallet.address,
  amount: 5_000_000n,
});

if (prepared.willCreateAta) {
  console.log('budget for an extra CreateIdempotent in your fee math');
}

// `prepared.builder` is an unsigned Umi TransactionBuilder.
// Hand it to your signer of choice and call `.sendAndConfirm(umi)` when ready.
const result = await prepared.builder.sendAndConfirm(umi);
Example — drain everything safely, no-op when empty:
import { prepareWithdrawTreasuryAll } from '@leash/registry-utils';

const prepared = await prepareWithdrawTreasuryAll(umi, {
  agentAsset: 'CoreAss…Asset',
  mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
  destination: ownerWallet.address,
});

if (prepared === null) {
  console.log('nothing to withdraw');
} else {
  console.log('draining', prepared.amount.toString(), 'atomic units');
  await prepared.builder.sendAndConfirm(umi);
}
This is the contract the upcoming Leash HTTP API speaks: every mutating endpoint returns { unsignedTransaction, echo } from the corresponding prepare* function so the caller stays in control of signing and broadcasting.