Arete documentation
Actions
For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending.mdto the URL or sendingAccept: text/markdown.
For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /sandboxes/actions.md.
An agent changes a sandbox’s wallet through exactly one tool,
sandbox_invoke, with one of two actions: invoke or withdraw. Each needs a
grant, and each can be previewed first with
sandbox_preview, which takes the same arguments.
The loop is: discover → preview → invoke.
sandbox_statusandsandbox_rules: what the wallet holds, which actions this agent holds, and which programs and instructions are allowed.sandbox_derive, when the action needs an address: a PDA (computed locally, with no network call) or an associated token account.sandbox_preview: a dry run. Returnspasswith what the wallet would lose and the transaction it built, or the refusal.sandbox_invoke: runs it.
Pass u64 amounts as decimal strings.
invoke
Section titled “invoke”Named instructions of the programs your rules enable, encoded from each
program’s IDL. One to 32 instructions per action, named exactly as
sandbox_rules lists them, as long as they fit one transaction: 4096
bytes and 64 accounts for v1 (the default), 1232 bytes for v0. A flow
that needs its setup, refreshes and the action together, such as a
first lending deposit, goes in one action. The sandbox wallet is the
only signer.
{ "action": "invoke", "args": { "instructions": [ { "program": "Pump.fun", "instruction": "buy", "args": { "...": "as sandbox_rules lists them" }, "accounts": { "...": "only those without a fill" } } ], "computeUnitPriceMicroLamports": "1000" }}| Argument | Meaning |
|---|---|
instructions[] | { program, instruction, args?, accounts?, remainingAccounts? } |
remainingAccounts | [{ address, writable? }], for instructions that take a variable account list |
computeUnitLimit | Optional compute budget |
computeUnitPriceMicroLamports | Optional priority fee, per compute unit |
transactionFormat | "v1" (default) or "v0" |
Accounts with a fill in sandbox_rules can be left out: Arete fills
fixed addresses, the wallet, PDAs, token programs, your associated token
accounts and fields it reads from the accounts you give (a pool’s mints and
vaults). A request still missing accounts is refused with
sandbox-intent-invalid, missingAccounts, and accountGuidance that
says how to find each one. See Finding accounts.
There is no separate swap action. A swap is an invoke of the instructions
that make it, so it passes the same rules, limits and checks as any other
instruction:
- Resolve the venue.
- A venue’s own instructions, when you know the pool: for example
PumpSwap (pump-amm)
buyandsellfor a coin that has left its Pump.fun bonding curve, Pump.funbuyandsellon the curve, or a DEX’s own swap instruction (Orca Whirlpool, Raydium, Meteora). This is the simplest path. - A Jupiter route, when you want the best route across venues: quote
with Jupiter’s Swap API, then ask it for
/swap-instructionswith the sandbox wallet as the user. Ask the quote for about 50maxAccounts, so the route fits a v1 transaction (64 accounts, no lookup tables). The route only fills through venues whose programs your rules enable.
- A venue’s own instructions, when you know the pool: for example
PumpSwap (pump-amm)
- Write the
invoke. Name each instruction assandbox_ruleslists it (Jupiter’s swap is one of the Jupiter instructions there, such asroute_v2orshared_accounts_route_v2), with itsargsdecoded from the instruction data by the IDL typessandbox_rulesgives, its named accounts, and any further accounts the route passes inremainingAccounts. Setup Jupiter returns, such as creating a token account or wrapping SOL, is baseline plumbing: put it in the same action, in order. sandbox_previewwithaction: "invoke"until it passes, thensandbox_invokewith the same arguments.
{ "action": "invoke", "args": { "instructions": [ { "program": "PumpSwap", "instruction": "sell", "args": { "...": "as sandbox_rules lists them" }, "accounts": { "...": "only those without a fill" } } ] }}Tokens the sandbox bought may always be sold, through any route the rules allow.
withdraw
Section titled “withdraw”SOL to your wallet, the only destination a withdraw can have.
{ "action": "withdraw", "args": { "lamports": "500000000" } }Transaction format
Section titled “Transaction format”Actions build Solana v1 transactions (SIMD-0385) by default: up to 4096 bytes
and 64 accounts, no address lookup tables, with the compute budget in the
message itself. transactionFormat: "v0" builds the older format instead (up
to 1232 bytes). A transaction too large for its format is refused with rule
transaction-size.
Exactly once
Section titled “Exactly once”Every sandbox_invoke has an idempotency key: the agent’s own
(idempotencyKey, 1 to 128 characters) or a generated one, returned on
success and on error. A key never runs twice.
- The same request, the same key: safe to send again. It returns the
original result, success or refusal, in the same shape plus
replayed: true. An action still in flight returns its current status. A replayed refusal also carries the record’srejectCode, which can differ from itscode: go by the replayedcodeandretryable. A replayed outage isretryable: true, not a permanent refusal; retry it with a new key. See Errors and refusals. - A different request, a used key: refused, not retryable, with the id of
the action that has the key (
sandbox-duplicate-action, orsandbox-idempotency-key-reused). Use a new key.
After a timeout, the action may have run, so never retry it under a new key. Retry with the same key, or look it up first:
sandbox_actionwith theidempotencyKey.- If it ran, don’t repeat it. If it was refused, retry with a new key. If no action has the key, retry with the same key.
Older servers answer every repeat of a key, the same request included, with
sandbox-duplicate-action and the action’s id; look it up with
sandbox_action for its result.
Results
Section titled “Results”A landed action returns its id, status, signature and outflowLamports:
everything that left the wallet, the network fee included.
networkFeeLamports is the network fee alone, null until the landed
transaction is read. feeLamports, when present, is something else: the
protocol’s own fees. It also names the guardPipeline that checked it, such
as execute-v1. sandbox_action lookups and history items carry
outflowLamports, networkFeeLamports and guardPipeline too (not
feeLamports), and previews carry outflowLamports and guardPipeline.
guardPipeline is null or absent on actions recorded before it was kept and
on actions that never reached the guard, such as a rejected one. The full record (decoded
instructions, events, balance changes) is in the
history. A refusal is a tool error with code,
rule, what triggered it, retryable and a hint; see
Errors and refusals.