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.
| Integration | Best for |
|---|---|
REST GET /api/v2/swap/quote | wallets/apps that don't want to read contracts themselves; returns JSON with a ready tx |
SDK /swap/sdk/btcw-swap.js | ES 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, to | token address or native (symbols also work, for manual testing only) |
amount | integer, base units — 1 BTCw = 1000000000000000000 |
sender | the signing wallet. When set, the response includes a ready-to-send tx, checks balance + approval, and simulates the transaction (simulation) |
recipient | receiver (defaults to sender). OracleAMM only pays the sender, so a different recipient leaves only the ClpPools route |
slippage_bps | 1–5000, default 100 (1%) → minAmountOut |
route | best (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.
Deep link
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.