Guides › Swap through contracts
Call the verified contracts directly — no API, no integration fee. You need a wallet with a little BTCw for gas. If you want the API to build transactions for you, see Wallet swap integration.
General flow
- Quote with a
quote…function (view, free). - Compute
minOut= quote × (1 − acceptable slippage). Every swap function takes this parameter; if the price moves too far it reverts withSlippageExceeded, costing nothing but gas. - If selling an ERC-20 token:
approvethe exact amount to the executing contract. - Send the swap. Native BTCw is attached as
msg.value.
There is no on-chain deadline — minOut is the only protection.
OracleAMM — BTCw ⇄ USDw
Address 0xd953d53414a81eb6AC5a4edE73d13992181aF51E. At most 10 BTCw per trade; pays the sender.
function quoteSellBtcw(uint256 btcwIn) view returns (uint256 usdOut)
function quoteBuyBtcw(uint256 usdIn) view returns (uint256 btcwOut)
function sellBtcw(uint256 minUsdOut) payable returns (uint256 usdOut) // send BTCw as msg.value
function buyBtcw(uint256 usdIn, uint256 minBtcwOut) returns (uint256 btcwOut) // approve USDw to OracleAMM first
import { JsonRpcProvider, Wallet, Contract, parseEther } from "ethers";
const p = new JsonRpcProvider("https://rpc.btcw.tech", 482120);
const w = new Wallet(process.env.PRIVATE_KEY, p);
const amm = new Contract("0xd953d53414a81eb6AC5a4edE73d13992181aF51E", [
"function quoteSellBtcw(uint256) view returns (uint256)",
"function sellBtcw(uint256) payable returns (uint256)",
], w);
const inAmt = parseEther("0.001");
const quote = await amm.quoteSellBtcw(inAmt); // USDw, 18 decimals
const tx = await amm.sellBtcw(quote * 99n / 100n, { value: inAmt }); // accept 1% slippage
await tx.wait();
ClpPools — every pair
Address 0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0. Every pool is paired with BTCw; token → token takes two hops in one transaction.
function quoteBtcwForAsset(address token, uint256 btcwIn) view returns (uint256 out, uint256 fee)
function quoteAssetForBtcw(address token, uint256 amountIn) view returns (uint256 out, uint256 fee)
function swapBtcwForAsset(address token, uint256 minOut, address to) payable returns (uint256)
function swapAssetForBtcw(address token, uint256 amountIn, uint256 minOut, address to) returns (uint256)
function swapAssetForAsset(address tokenIn, address tokenOut, uint256 amountIn, uint256 minOut, address to) returns (uint256)
Quoting token → token: call quoteAssetForBtcw(tokenIn, amountIn), then quoteBtcwForAsset(tokenOut, result).
const clp = new Contract("0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0", [
"function quoteBtcwForAsset(address,uint256) view returns (uint256 out, uint256 fee)",
"function swapBtcwForAsset(address,uint256,address) payable returns (uint256)",
], w);
const token = "0x…"; // address from /api/v2/swap/assets
const [out] = await clp.quoteBtcwForAsset(token, parseEther("0.01"));
await (await clp.swapBtcwForAsset(token, out * 99n / 100n, w.address, { value: parseEther("0.01") })).wait();
With Foundry cast
# quote 0.01 BTCw → token
cast call 0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0 "quoteBtcwForAsset(address,uint256)(uint256,uint256)" $TOKEN 10000000000000000 --rpc-url https://rpc.btcw.tech
# sell token → BTCw: approve the exact amount, then swap
cast send $TOKEN "approve(address,uint256)" 0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0 $AMOUNT --rpc-url https://rpc.btcw.tech --private-key $PK
cast send 0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0 "swapAssetForBtcw(address,uint256,uint256,address)" $TOKEN $AMOUNT $MIN_OUT $ME --rpc-url https://rpc.btcw.tech --private-key $PK
Multi-hop in one transaction — SwapRouter
Address 0x397E607eA05eF7909f84540077F3a716778031E4. The contract is ownerless, with no mutable parameters. It chains up to 4 hops, each hop being
(venue, tokenOut): venue 0 is ClpPools, 1 is OracleAMM (BTCw ⇄ USDw), 2 and above are the oracle-priced token vaults in tokenAmmAt(i) order (currently venue 2 = ETHw vault).
Native BTCw is written as 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.
function quote(address tokenIn, uint256 amountIn, (uint8 venue, address tokenOut)[] hops) view returns (uint256 amountOut)
function swap(address tokenIn, uint256 amountIn, (uint8 venue, address tokenOut)[] hops, uint256 minOut, address to, uint256 deadline)
payable returns (uint256 amountOut)
- BTCw in: send
msg.value = amountIn. Token in:approvethe exact amount to the router — the router only pulls frommsg.sender. quotedoes not check per-trade caps, inventory or pause state — the SDK and the quote API check those beforehand.deadlineis a Unix timestamp; past it, the call reverts withExpired.
# 0.1 BTCw → ETHw: OracleAMM (BTCw → USDw), then the ETHw vault (USDw → ETHw), in one transaction
cast call 0x397E607eA05eF7909f84540077F3a716778031E4 "quote(address,uint256,(uint8,address)[])(uint256)" \
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE 100000000000000000 \
"[(1,0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727),(2,0xd8fA8202ecC699687182109084B9ECBb44025759)]" --rpc-url https://rpc.btcw.tech
The quote API and the SDK (router route) find the path and return a ready-made router transaction — see Wallet swap integration.
Tracking results
// SwapRouter
event Routed(address indexed sender, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut, address to, uint256 hops)
// ClpPools
event Swap(address indexed trader, address indexed token, bool btcwIn, uint256 amountIn, uint256 amountOut, uint256 fee, address to)
// OracleAMM
event Swap(address indexed trader, bool btcwIn, uint256 amountIn, uint256 amountOut, uint256 priceWad)
Common errors and revert codes: Limits & error codes. Try it first on a chain fork: Test on a chain fork.