Giving an AI Agent the Ability to Send Solana Transactions

Bounded authority, deterministic execution, and the failure modes that appear when the thing deciding to trade is non-deterministic.

BoltTx Team··10 min read
solanaai-agentautomationtransaction-landingsecuritytrading-bot

Connecting a language model to a wallet is a few lines of code. Doing it so that a bad output costs you a bounded amount is the entire engineering problem.

The framing that keeps this tractable: the agent decides, and something deterministic executes. Blur that boundary and every model failure becomes a fund-loss failure.

Two Layers, One Boundary

┌───────────────────────────────┐
│ agent: reasoning, non-deterministic │
│ ★produces intent, not transactions★  │
└───────────────┬───────────────┘
                │ validated intent
┌───────────────▼───────────────┐
│ executor: deterministic code   │
│ ★builds, signs, submits, retries★   │
└───────────────────────────────┘

★The agent must never hold a private key, and must never emit a signed transaction.★ It emits a structured intent, the executor validates it against rules the agent cannot modify, and only then does anything get signed.

// The only thing the agent produces.
type Intent = {
  action: "swap" | "close_position";
  inputMint: string;
  outputMint: string;
  amountLamports: bigint;
  maxSlippageBps: number;
  reason: string;              // ★logged, never trusted★
};

The reason field is worth including and worth never acting on. It is what makes an audit possible after an odd trade, and treating it as justification is how a persuasive explanation ends up substituting for a validity check.

Validate Every Intent

The executor's validation is the actual safety mechanism. It runs on every intent, regardless of how confident the agent sounded.

function validate(intent: Intent, state: AgentState): void {
  // ★Hard bounds — not suggestions the agent can argue with.★
  if (intent.amountLamports > LIMITS.maxPerTrade) throw new Error("size");
  if (state.spentToday + intent.amountLamports > LIMITS.maxDaily)
    throw new Error("daily cap");
  if (!ALLOWED_MINTS.has(intent.outputMint)) throw new Error("mint");
  if (intent.maxSlippageBps > LIMITS.maxSlippage) throw new Error("slippage");
  if (state.tradesThisHour >= LIMITS.maxHourly) throw new Error("frequency");
}

★The frequency limit is the one people leave out, and it is the one that catches loops.★ An agent that misreads its own position and decides to buy — repeatedly, each time reasoning correctly from a wrong premise — will drain a wallet through a size limit that individually approves every trade.

A daily cap and an hourly count are what turn "the model was wrong" into a bounded loss.

Bound the Authority On Chain

Application-level limits protect against a confused agent. They do not protect against a compromised process, because code that holds a key can ignore its own checks.

Options in increasing order of strength:

A dedicated wallet with a small balance. Crude, effective, and immediately understandable — check it with getBalance and top it up deliberately. The maximum loss is what you funded it with.

Delegated token authority. Approve a specific amount to the agent's key so it can spend that and nothing more.

Program-enforced limits. A small on-chain program that rejects anything outside its parameters. ★The only option where compromising the agent process is not sufficient to take the funds.★

★The choice is a risk decision, not a technical one, and it should be made explicitly rather than by default.★ Most teams start with a funded hot wallet, and the important part is knowing that is what you chose.

If your executor is solid and trades are still missing, a free BoltTx key is one line to test the submission path.

Agents Are Slow, and That Changes the Design

Model inference takes seconds. Solana slots are shorter than that by a wide margin.

★This rules out an entire category of strategy.★ An agent cannot compete for a launch, cannot win an arbitrage race, and cannot react inside a single block. Anything latency-competitive belongs in deterministic code.

What agents are genuinely good at is the layer above: interpreting conditions, weighing tradeoffs that are hard to express as thresholds, deciding whether rather than when.

// ★Agent sets policy. Deterministic code executes it.★
const policy = await agent.decide(marketContext);
// → { action: "reduce_exposure", target: 0.5, urgency: "high" }

await executor.rebalance(policy);   // fast path, no model in the loop

Anything requiring a reaction within a slot must not have a model call on that path — no inference between getSlot and sendRawTransaction. This is not a limitation to engineer around; it is a boundary to design to.

Idempotency Against Repetition

Agents repeat themselves. A retry loop, a re-prompt, or an ambiguous state read all produce the same intent twice.

// ★Deduplicate on intent, not on transaction.★
const key = hashIntent(intent);
if (await recentlyExecuted(key, windowMs)) {
  return { skipped: "duplicate intent" };
}

★Solana's signature-level replay protection does not help here★, because the second attempt is a genuinely different transaction — new blockhash, new signature — expressing the same decision. Deduplication has to happen at the intent layer, before anything is built.

Pair it with resolving each submission to a terminal state, so the executor knows whether a prior attempt actually landed:

switch (outcome.status) {
  case "success":  await recordExecuted(key, outcome.slot); break;
  case "reverted": await recordFailed(key, outcome.err); break;   // ★do not auto-retry★
  case "expired":  await maybeRetry(key, intent); break;          // ★safe to retry★
}

Never feed a revert straight back to the agent as "try again." A revert means the state rejected the action, and an agent asked to retry will usually reason its way to the same intent with more conviction.

Logging for Something Non-Deterministic

Standard transaction logs are insufficient here, because the interesting question is not what was submitted but why.

Log Why it matters
Full input context ★Reproduce the decision★
Raw model output Distinguish bad reasoning from bad parsing
Validated intent What the executor actually saw
Rejections and which rule fired ★Where your limits are binding★
Signature and terminal state The chain outcome
Model and prompt version Attribute changes in behaviour

★The rejection log is the one that earns its keep.★ A rising rejection rate against a specific rule is the earliest available signal that the agent's behaviour has drifted — and it arrives before any money is lost, unlike every other metric here.

What Landing Looks Like

Real transactions through our delivery nodes: median confirmation 336ms — under one slot.

★For an agent system, predictable submission is what lets you attribute failures correctly.★ When execution is unreliable, every bad outcome is ambiguous between a wrong decision and a lost transaction — and debugging a non-deterministic component with noisy feedback is close to impossible.

Where BoltTx Fits

We handle submission. Not the agent, not custody, not your validation rules.

Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so transactions are not observable in transit before they land.

Your executor signs locally with whatever key model you chose. We never hold funds, never sign, and never modify transaction contents — the agent's authority stays entirely inside your own bounds. The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.

Get a free API key. No monthly fee:

const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

FAQ

How do I let an AI agent trade on Solana safely? Separate decision from execution. The agent emits a structured intent, deterministic code validates it against limits the agent cannot modify, and only that code signs and submits.

Should an AI agent hold a private key? No. Keys belong to the executor, which enforces bounds the agent has no ability to change. An agent with a key means every model failure is potentially a fund-loss failure.

What limits should I put on an agent wallet? Per-trade size, daily total, allowed mints, maximum slippage, and a frequency cap. The frequency cap is the one most often omitted and the one that stops a reasoning loop from draining a wallet through individually valid trades.

Can an AI agent compete in Solana arbitrage? Not on latency. Model inference takes seconds while slots are far shorter, so anything latency-competitive must run in deterministic code. Agents fit the policy layer above execution.

How do I stop an agent from repeating the same trade? Deduplicate on a hash of the intent within a time window. Signature-level replay protection does not help, since a repeat is a genuinely different transaction expressing the same decision.

Should a failed transaction be reported back to the agent? Report the outcome, but do not automatically ask it to retry a revert. A revert means the state rejected the action, and an agent asked to try again typically produces the same intent with more confidence.

How do I limit agent authority on chain? Options range from a dedicated wallet with a small balance, through delegated token authority, to a program that enforces parameters. Only the last one survives a compromise of the agent process itself.

What should I log for an AI trading agent? Input context, raw model output, validated intent, rejections with the rule that fired, the signature, and the terminal state. Without the first two, an odd trade cannot be reproduced.

How do I detect that an agent is behaving badly? Watch the rejection rate per rule. A rise against one limit is the earliest signal of drift, and unlike outcome-based metrics it appears before money is lost.

Can an agent set its own slippage? It can propose one, and the executor should clamp it to a hard maximum. A model persuaded that unusual conditions justify unusual slippage is exactly the case the clamp exists for.

What happens if the agent produces malformed output? The executor rejects it during validation and nothing is built or signed. Parsing failures should be logged separately from rule rejections, since they indicate a different problem.

Should agents run continuously or on a schedule? Scheduled or event-triggered is easier to bound. A continuously running agent needs a frequency cap regardless, and the cap is doing the same work either way.

How do I test an agent trading system? Test the executor deterministically with adversarial intents — oversized, disallowed mints, extreme slippage, rapid repeats — and confirm every one is rejected. Validation is the component that must be correct.

Does the agent need to know about blockhash expiry? No, and it should not. Expiry, retries, and terminal states belong entirely to the executor. Exposing chain mechanics to the agent adds a way for it to reason incorrectly about them.

How do I handle an agent that wants to trade during congestion? The executor decides fee policy, not the agent. Derive it from recent fees on the accounts involved, cap it, and treat hitting the cap as a signal rather than something to silently pay through.

What is the minimum safe setup for an agent with a wallet? A dedicated wallet funded with an amount you can lose entirely, hard limits in the executor including a frequency cap, intent deduplication, and logging complete enough to reconstruct any decision.

Back to all posts