BETA — mainnet live (chain 482120); features are still being finished. Transparency →
Bitcoin Swap BETA Chain 482120
Developers · Guides

Swaps for wallets

A THORChain-style quote API that returns ready-to-sign transactions — any pair of the 202 assets.

Guides › Swaps for wallets

This follows the THORChain model for opening swaps to wallets: the wallet calls one quote endpoint and gets back the amount out, fees, route and a prebuilt transaction to sign. Any pair among the 202 assets (native BTCw, USDw, 100 …w tokens) can be swapped, in both directions. Transactions call the verified ClpPools / OracleAMM contracts directly: no intermediary router, no integration fee, no API key.

IntegrationBest for
REST GET /api/v2/swap/quotewallets/apps that don't want to read contracts themselves; returns JSON with a ready tx
SDK /swap/sdk/btcw-swap.jsES module, no dependencies; quotes are read straight from the contracts over RPC, so you don't have to trust any server. The API uses this same file, so both always return the same numbers
Deep link /swap/?from=…&to=…&amount=…wallets with a dApp browser: opens the Swap page, wallet detected via EIP-6963 / window.ethereum

Asset list

GET https://btcw.tech/api/v2/swap/assets
→ { chainId: 482120, assets: [ { address, native, symbol, name, decimals, logoURI, priceUsd, poolDepthBtcw }, … ] }   // 102 entries

Native BTCw uses the conventional address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE (native, BTCw and 0x000…0 are also accepted). All assets have 18 decimals. Identify assets by chainId + address, not by symbol.

Quote — GET /api/v2/swap/quote

Parameter
from, totoken address or native (symbols also work, for manual testing only)
amountinteger, base units — 1 BTCw = 1000000000000000000
senderthe signing wallet. When set, the response includes a ready-to-send tx, checks balance + approval, and simulates the transaction (simulation)
recipientreceiver (defaults to sender). OracleAMM only pays the sender, so a different recipient leaves only the ClpPools route
slippage_bps1–5000, default 100 (1%) → minAmountOut
routebest (default) · clp · amm · amm-eth · router
curl -s "https://btcw.tech/api/v2/swap/quote?from=native&to=0xd8fA8202ecC699687182109084B9ECBb44025759&amount=1000000000000000000&sender=0xYourWallet"
{
  "chainId": 482120, "from": "0xEeee…EEeE", "to": "0xd8fa…5759", "amountIn": "1000000000000000000",
  "venue": "clp", "amountOut": "30126980820155435233", "minAmountOut": "29825711011953880880",
  "legs": ["0xEeee…EEeE", "0xd8fa…5759"],               // token → token: 3 entries, BTCw in the middle
  "fees": [{ "asset": "0xd8fa…5759", "amount": "1102883046289534940" }],
  "priceImpactBps": 353,                                 // slippage + fee vs. the pool mid price
  "oracleDeviationBps": -333,                            // vs. the oracle price
  "routes": [ { "venue": "clp", "amountOut": "…" }, { "venue": "amm", "error": "…" } ],
  "approval": null,                                      // token in: { token, spender, amount, needed, tx }
  "tx": { "chainId": 482120, "from": "0x…", "to": "0x557a…E7e0", "data": "0x3428bb69…", "value": "0xde0b6b3a7640000" },
  "simulation": { "ok": true }, "expiresAt": 1790738178, "warnings": []
}

Wallet flow: if approval.needed, send approval.tx first (approve the exact amount, not unlimited), wait for it to be mined, request a new quote, then send tx. There is no on-chain deadline — minAmountOut is the protection; if the user waits past expiresAt (30 seconds), request a new quote. If the price moves beyond the slippage tolerance, the transaction reverts with SlippageExceeded (0x71c4efed) and nothing is lost except gas. Invalid input returns HTTP 400 with error; if no route can fill the order, 422. Rate limit: 20 requests/second per IP.

SDK

import { getQuote, ADD_CHAIN_PARAMS, waitForReceipt } from "https://btcw.tech/swap/sdk/btcw-swap.js";

await ethereum.request({ method: "wallet_addEthereumChain", params: [ADD_CHAIN_PARAMS] });
const q = await getQuote({ from: "native", to: "0xd8fA8202ecC699687182109084B9ECBb44025759",
                           amount: 10n ** 18n, sender: me, slippageBps: 100 });
if (q.error) throw new Error(q.error);
if (q.approval?.needed) await waitForReceipt(await ethereum.request({ method: "eth_sendTransaction", params: [q.approval.tx] }));
const hash = await ethereum.request({ method: "eth_sendTransaction", params: [q.tx] });

Also exported: getAssets(), decodeError(err) (maps a revert code to its reason), CONTRACTS, CHAIN. Importing directly from btcw.tech works from any domain (the file is served with CORS); for a fixed build, copy the file and pin the version (VERSION) — see SDK.

Tested on a fork of the live chain: 7 order types (CLP single hop, token→token two hops, forced OracleAMM both directions, pay to another wallet) — amounts received matched the quote to the wei; a stale transaction sent after the price moved was correctly blocked with SlippageExceeded.

Wallets with a dApp browser can open the Swap page directly with the pair and amount prefilled:

https://btcw.tech/swap/?from=BTCw&to=USDw&amount=1
# from / to: symbol or token address; amount: human-readable number (not base units)

The page detects the wallet via EIP-6963 or window.ethereum. Full API parameter reference: REST API; SDK: JavaScript SDK.