Blog'a geri dön

·7 dk. okuma

Give Your Agent Bitcoin: Meet the BOB Gateway CLI

Dominik Harz

Dominik Harz

Co-founder at BOB

Give Your Agent Bitcoin: Meet the BOB Gateway CLI

Add native BTC swaps to any AI agent with the Gateway CLI - no SDK binding, no language lock-in, no custody of user keys.

Paylaşmak

A new pattern for interacting onchain is emerging, and it isn't apps or websites at all. It's AI agents. Agents don't browse and click. They call tools. They read the result. They act. And right now, no agent toolchain ships a native way to move Bitcoin. The Bitcoin slot is empty.

BOB Gateway is the best swap engine for native BTC. It moves Bitcoin to and from EVM chains in one Bitcoin transaction, non-custodial, cheap, fast, and programmable.

We want it to be the engine agents reach for the moment they need to touch Bitcoin. And the fastest way to get there already exists: the Gateway CLI.

A Good CLI is a Great Agent Tool

An agent is very good at one thing. It calls a tool and reads what comes back. A command-line program with clean, structured output is exactly that tool. There is no SDK to bind, no language to match, no service to host. Anything that can run a command can run a swap: a Python agent, a TypeScript agent, a shell script, a scheduled job.

We built the BOB Gateway CLI so that interface is clean for a machine, not just a person.

  • JSON on every command. Add --json to any quote, swap, balance, route, or status call and get structured output back. Errors come back structured too, so an agent can react to a failure instead of parsing prose.
  • Unsigned by default when you want it. The --unsigned flag returns an unsigned Bitcoin PSBT or EVM transaction and stops. Your agent never holds a key. It passes the unsigned payload to whatever signer or policy you trust, and that layer decides whether to sign.
  • Fire and forget. With --no-wait, the CLI submits a swap and returns an order ID instead of blocking until settlement. The agent moves on and checks status later.
  • Amounts in dollars. --amount 100USD resolves to the right amount of BTC at the current price. "Move a hundred dollars of Bitcoin to Base" maps almost word for word onto a command.
  • Config from the environment. Keys and endpoints come from environment variables, with no config file left behind. That is how you inject secrets into a sandbox or container cleanly.

None of this is an AI feature. It is good CLI design, which turns out to be most of what an agent needs.

Wiring it into an Agent

The shape of an agentic swap is simple. Discover what is possible, get a quote, decide, then settle. Here it is with real commands.

Discover the routes so the agent is not guessing at assets or chains:

gateway-cli routes --json

Get a quote before committing. This is the step an agent should always take, because slippage, fees, and the real receive amount become visible here:

gateway-cli quote --src BTC --dst USDC:base --amount 100USD --json

Settle without ever handing over a key. Return an unsigned transaction and pass it to your signer:

gateway-cli swap --src BTC --dst USDC:base --amount 100USD --unsigned --json

Or, if the CLI signs inside a trusted sandbox, add --no-wait so the agent is not blocked while the swap settles:

gateway-cli swap --src BTC --dst USDC:base --amount 100USD --no-wait --json
# returns an order id you poll later
gateway-cli status <order-id> --json

Exposing this to a model takes a few lines. A tool that shells out and returns parsed JSON is enough:

import subprocess, json

def gateway_swap(src: str, dst: str, amount: str) -> dict:
    """Swap native BTC and EVM tokens via BOB Gateway. Returns an unsigned tx for your signer to approve."""
    out = subprocess.run(
        ["gateway-cli", "swap", "--src", src, "--dst", dst,
         "--amount", amount, "--unsigned", "--json"],
        capture_output=True, text=True, check=True,
    )
    return json.loads(out.stdout)

Give your model that function plus matching quote and status tools and the loop runs itself. The agent quotes, reasons about the result, asks for an unsigned swap, your signer approves, and the agent tracks settlement to the end. "Swap $100 of BTC to USDC on Base" becomes a sequence of tool calls with a signature you control in the middle.

A Tiny Agent in the Wild: The Gateway Starter Bot

Abstract agents are easy to describe. Here is a concrete one.

The gateway-starter-bot is a single bash script - 179 lines - that runs a complete conditional BTC round-trip without a price API, without a framework, and without anything except the Gateway CLI and jq. It is the simplest non-trivial agent you can build on Gateway, and reading it top to bottom is the fastest way to understand what agentic BTC swaps actually look like in practice.

What it does

The bot executes a toy "buy the dip" strategy in three phases:

  1. Onramp. Swap a fixed dollar amount of BTC into USDT on Ethereum.
  2. Watch. Poll the reverse quote - USDT back to BTC - on a configurable interval.
  3. Buy. The moment the quote would return more satoshis than were originally sold (by a configurable threshold), execute the buyback and exit with more BTC than it started with.

The interesting design choice is that there is no external price feed. The gateway quote is the price signal. The bot sold a known number of satoshis; it buys back the instant the gateway would return more. Fees and slippage are already baked into every quote, so one tool provides one source of truth. It is deliberately limited - no resume logic, no scheduling, no portfolio management - and that restraint is the point. This is a starting point, not a finished product.

How it uses the CLI

Every meaningful action in the bot is a gateway-cli ... --json call piped through jq. That is it. There is no SDK, no library, no event loop. The full pattern:

# Get a quote to simulate the onramp (dry-run) or execute it (live)
onramp=$(cli quote --src BTC --dst USDT:ethereum --amount 100USD --json)
BTC_SOLD_SAT=$(jq -r '.srcAmount' <<<"$onramp")
USDT_AMOUNT=$(jq -r '.dstAmount' <<<"$onramp")

# Poll the reverse quote until the dip condition is met
quote=$(cli quote --src USDT:ethereum --dst BTC --amount "$USDT_AMOUNT" --json)
BTC_OUT_SAT=$(jq -r '.dstAmount' <<<"$quote")

# Execute the buyback
buyback=$(cli swap --src USDT:ethereum --dst BTC --amount "$USDT_AMOUNT" --json)

The dip condition is integer arithmetic - no floating point, no external price oracle. All gateway amounts are atomic integers (satoshis for BTC, six-decimal units for USDT), so the comparison is exact:

# buy when: btc_out_sat * 10000 >= btc_sold_sat * (10000 + DIP_THRESHOLD_BPS)
TARGET_SAT=$(( BTC_SOLD_SAT * (10000 + DIP_THRESHOLD_BPS) / 10000 ))

This is the agentic loop reduced to its minimum: get structured data, make a decision, act.

Safe to run from the first minute

The bot ships with a --dry-run flag that runs the entire strategy - onramp simulation, watch loop, buyback simulation - using quotes only. No funds move. The output is identical to a live run:

! DRY RUN — no real swaps will be executed. Quotes only.
▸ Trade size 100USD | dip threshold 500bps | poll 60s | max wait 3600s
▸ Onramp: 100USD of BTC -> USDT on Ethereum
✓ Sold 0.00094000 BTC (94000 sats), now holding USDT (atomic: 99830000)
▸ Will buy back when a quote returns >= 0.00098700 BTC (98700 sats)
▸ No dip yet: quote 0.00094100 BTC < target 0.00098700 BTC. Waiting 60s…
✓ Dip detected: quote returns 0.00098750 BTC (>= target)
✓ [dry-run] Would receive 0.00098750 BTC (98750 sats)
▸ Summary:
  sold:   0.00094000 BTC (94000 sats)
  bought: 0.00098750 BTC (98750 sats)
  net:    +0.00004750 BTC (4750 sats)

All configuration - trade size, threshold, poll interval, timeout - lives in environment variables. The minimum Gateway trade size is $25.

Use a throwaway wallet with a small balance for any live run. The CLI derives addresses from the private keys directly there is nothing else to configure.

The CLI is the Start, not the Finish

The CLI is the fastest path, not the only one. The same Gateway engine sits behind a TypeScript SDK and a public API, and it is ready to sit behind the model-context surfaces that are quickly becoming how models get tools. Wrap the engine as an MCP server and you have one Bitcoin-bridge tool that works across every MCP client. The CLI is a clean blueprint for exactly what that tool needs to expose.

We have already run experiments here, including a Gateway-powered yield bot built on Coinbase's AgentKit. Frameworks come and go faster than anyone can track. The engine underneath does not. Whether an agent reaches Gateway through a CLI, an MCP server, or next year's framework, it is calling the same swaps engine for native BTC. Build the Bitcoin layer once and let the toolchains find it.

Try it

npm install -g @gobob/gateway-cli
gateway-cli --help

Set BITCOIN_PRIVATE_KEY and EVM_PRIVATE_KEY, or stay entirely in --unsigned mode, and native Bitcoin swaps are available to anything that can run a command. If you are building an agent that needs to touch BTC, that is the whole integration.

The Bitcoin slot is open. Fill it with BOB Gateway, and show us what you build.

Paylaşmak
Dominik Harz

Dominik Harz

Co-founder at BOB

10 years smart contracts, stablecoins & DeFi R&D. Computer Science PhD from Imperial College. Previously cybersecurity at PWC, BinanceX Fellow.