{
  "name": "Bitcoin Swap (BETA)",
  "updated": "2026-09-30",
  "notice": "BETA. Match assets by chainId + contract address, never by symbol (BTCW/USDW are used by unrelated assets elsewhere).",
  "chains": {
    "bitcoinswap": {
      "chainId": 482120,
      "chainIdHex": "0x75b48",
      "hyperlaneDomain": 482120,
      "rpc": "https://rpc.btcw.tech",
      "explorer": "https://explorer.btcw.tech",
      "chainJson": "https://btcw.tech/chain.json",
      "nativeCurrency": {
        "name": "Bitcoin Swap",
        "symbol": "BTCw",
        "decimals": 18
      },
      "minGasPriceWei": "1000000",
      "rpcNamespaces": [
        "eth",
        "net",
        "web3"
      ],
      "rpcRateLimit": "10 requests/s per IP (batch = 1 request)"
    },
    "arbitrum": {
      "chainId": 42161,
      "hyperlaneDomain": 42161,
      "rpc": "https://arb1.arbitrum.io/rpc",
      "explorer": "https://arbiscan.io"
    }
  },
  "priceApi": {
    "btcw": "https://btcw.tech/api/price",
    "tokens": "https://btcw.tech/api/tokens",
    "health": "https://btcw.tech/api/health",
    "pools": "https://btcw.tech/api/v2/pools",
    "tokenlist": {
      "url": "https://btcw.tech/tokenlist.json",
      "status": "coming soon"
    }
  },
  "swapApi": {
    "about": "Quote any pair of the 202 assets (native BTCw, USDw, 100 crypto and 100 US stock tracker tokens) and get a ready-to-sign transaction that calls ClpPools or OracleAMM directly. No router, no integrator fee, no API key. Same logic as the SDK.",
    "assets": "https://btcw.tech/api/v2/swap/assets",
    "quote": "https://btcw.tech/api/v2/swap/quote?from={address|native}&to={address|native}&amount={baseUnits}&sender={wallet}&recipient={optional}&slippage_bps={1..5000, default 100}&route={best|clp|amm}",
    "sdk": "https://btcw.tech/swap/sdk/btcw-swap.js",
    "nativeAsset": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
    "decimals": 18,
    "flow": "If approval.needed, send approval.tx (exact amount), wait, re-quote; then send tx. minAmountOut protects against price moves (revert SlippageExceeded 0x71c4efed); re-quote after expiresAt. OracleAMM pays msg.sender only, so a different recipient restricts the route to ClpPools.",
    "errors": "HTTP 400 invalid input, 422 no route can fill, 502 RPC error. Rate limit 20 req/s per IP.",
    "deepLink": "https://btcw.tech/swap/?from={symbol|address}&to={symbol|address}&amount={decimal}"
  },
  "contracts": {
    "bitcoinswap": {
      "USDw": {
        "address": "0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727",
        "decimals": 18,
        "abi": [
          "function name() view returns (string)",
          "function symbol() view returns (string)",
          "function decimals() view returns (uint8)",
          "function totalSupply() view returns (uint256)",
          "function balanceOf(address) view returns (uint256)",
          "function allowance(address,address) view returns (uint256)",
          "function approve(address spender, uint256 amount) returns (bool)",
          "function transfer(address to, uint256 amount) returns (bool)",
          "event Transfer(address indexed from, address indexed to, uint256 value)"
        ]
      },
      "OracleAMM": {
        "address": "0xd953d53414a81eb6AC5a4edE73d13992181aF51E",
        "about": "BTCw <-> USDw at the oracle price +/- spreadBps (30 = 0.3%). maxTradeBtcw per order; stops when the oracle is older than maxAgeSec.",
        "abi": [
          "function quoteSellBtcw(uint256 btcwIn) view returns (uint256 usdOut)",
          "function quoteBuyBtcw(uint256 usdIn) view returns (uint256 btcwOut)",
          "function sellBtcw(uint256 minUsdOut) payable returns (uint256 usdOut)",
          "function buyBtcw(uint256 usdIn, uint256 minBtcwOut) returns (uint256 btcwOut)",
          "function midPrice() view returns (uint256 priceWad, uint256 updatedAt)",
          "function spreadBps() view returns (uint256)",
          "function maxTradeBtcw() view returns (uint256)",
          "function btcwReserve() view returns (uint256)",
          "function usdReserve() view returns (uint256)",
          "function paused() view returns (bool)",
          "event Swap(address indexed trader, bool btcwIn, uint256 amountIn, uint256 amountOut, uint256 priceWad)"
        ]
      },
      "OracleTokenAMM_ETHw": {
        "address": "0xFd6d78A3b60175D37146793396A2FA7864039D54",
        "about": "ETHw <-> USDw at the PriceHub price of ETHw +/- spreadBps (30 = 0.3%). maxTradeToken 300 ETHw per order; stops when the price is older than maxAgeSec (300). buyToken/sellToken can pay another recipient. Bridge destination for ETHw (deposit USDC/USD₮0 -> USDw -> ETHw).",
        "abi": [
          "function quoteBuyToken(uint256 usdIn) view returns (uint256 tokenOut)",
          "function quoteSellToken(uint256 tokenIn) view returns (uint256 usdOut)",
          "function buyToken(uint256 usdIn, uint256 minTokenOut, address to) returns (uint256 tokenOut)",
          "function sellToken(uint256 tokenIn, uint256 minUsdOut, address to) returns (uint256 usdOut)",
          "function midPrice() view returns (uint256 priceWad, uint256 updatedAt)",
          "function token() view returns (address)",
          "function usd() view returns (address)",
          "function hub() view returns (address)",
          "function tokenReserve() view returns (uint256)",
          "function usdReserve() view returns (uint256)",
          "function spreadBps() view returns (uint256)",
          "function maxAgeSec() view returns (uint256)",
          "function maxTradeToken() view returns (uint256)",
          "function paused() view returns (bool)",
          "event Swap(address indexed trader, address indexed to, bool tokenIn, uint256 amountIn, uint256 amountOut, uint256 priceWad)",
          "error ContractPaused()",
          "error ZeroAmount()",
          "error ZeroAddress()",
          "error StalePrice(uint256 updatedAt)",
          "error InvalidOraclePrice()",
          "error ExceedsMaxTrade(uint256 cap, uint256 amount)",
          "error InsufficientLiquidity(uint256 available, uint256 required)",
          "error SlippageExceeded(uint256 amountOut, uint256 minAmountOut)"
        ]
      },
      "SwapRouter": {
        "address": "0x397E607eA05eF7909f84540077F3a716778031E4",
        "about": "Ownerless router: chains up to MAX_HOPS (4) hops across venue 0 ClpPools, venue 1 OracleAMM (BTCw <-> USDw), venue 2 token AMMs (ETHw kho) in one transaction. BTCw in: send msg.value = amountIn; token in: approve the router (it only pulls from msg.sender). quote() does not check caps, reserves or pause.",
        "abi": [
          "function swap(address tokenIn, uint256 amountIn, (uint8 venue, address tokenOut)[] hops, uint256 minOut, address to, uint256 deadline) payable returns (uint256 amountOut)",
          "function quote(address tokenIn, uint256 amountIn, (uint8 venue, address tokenOut)[] hops) view returns (uint256 amountOut)",
          "function tokenAmmCount() view returns (uint256)",
          "function tokenAmmAt(uint256 index) view returns (address amm, address token)",
          "function clp() view returns (address)",
          "function oracleAmm() view returns (address)",
          "function usdw() view returns (address)",
          "function NATIVE() view returns (address)",
          "function MAX_HOPS() view returns (uint256)",
          "event Routed(address indexed sender, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut, address to, uint256 hops)",
          "error Expired(uint256 deadline)",
          "error InvalidRecipient()",
          "error InvalidPath()",
          "error UnknownVenue(uint8 venue)",
          "error UnsupportedHop(uint8 venue, address tokenIn, address tokenOut)",
          "error ValueMismatch(uint256 sent, uint256 expected)",
          "error SlippageExceeded(uint256 amountOut, uint256 minAmountOut)",
          "error ZeroAmount()",
          "error ZeroAddress()",
          "error UnexpectedSender()",
          "error NativeTransferFailed()"
        ]
      },
      "ClpPools": {
        "address": "0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0",
        "about": "201 continuous-liquidity pools (USDw, 100 crypto trackers, 100 US stock trackers), every pool paired with native BTCw (THORChain-style CLP). Fee floor minFeeBps (5).",
        "abi": [
          "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)",
          "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 priceInBtcw(address token) view returns (uint256)",
          "function poolCount() view returns (uint256)",
          "function poolTokenAt(uint256 index) view returns (address)",
          "function pools(address token) view returns (uint256 btcwDepth, uint256 assetDepth, uint256 totalUnits, bool exists, bool enabled)",
          "function minFeeBps() view returns (uint256)",
          "function paused() view returns (bool)",
          "event Swap(address indexed trader, address indexed token, bool btcwIn, uint256 amountIn, uint256 amountOut, uint256 fee, address to)"
        ]
      },
      "BtcwPriceFeed": {
        "address": "0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c",
        "decimals": 8,
        "about": "BTC/USD, Chainlink AggregatorV3Interface. Internal oracle (median of external sources).",
        "abi": [
          "function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)",
          "function latestAnswer() view returns (int256)",
          "function decimals() view returns (uint8)"
        ]
      },
      "PriceHub": {
        "address": "0xE87FDdCC0b668564a20B23F3bE1EE108C805ecb4",
        "decimals": 18,
        "about": "USD reference price of the 200 tracker tokens: 100 crypto, 100 US stocks (internal oracle).",
        "abi": [
          "function latestRoundData(address asset) view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)",
          "function prices(address[] assets) view returns (int256[] answers, uint256[] updatedAts)",
          "function decimals() view returns (uint8)"
        ]
      }
    },
    "bridge": {
      "protocol": "Hyperlane warp route, collateral <-> collateral (@hyperlane-xyz/core 12.1.0), 1:1",
      "about": "Arbitrum USDC / USD₮0 (6 decimals) are locked in the Arbitrum router; the 482120 router releases USDw (18 decimals) from its reserve, and back. Amount leaving an end is bounded by what is locked there. On 482120 amounts must be a multiple of granularity (1e12). No interchain gas payment: call transferRemote with msg.value = 0. Recipient is bytes32, address LEFT-padded with zeros. Delivery usually 10-30 s. Read allowlistEnabled(): when true only allowlisted wallets can send.",
      "mailbox": {
        "bitcoinswap": "0x489DC54c2d2f7df9175d7F99716094C85E008392",
        "arbitrum": "0xBf4E868aE56870DC92dC906E1ad21ce038EF3198"
      },
      "routes": [
        {
          "name": "USDC",
          "arbitrum": {
            "router": "0x10B5757aC9a40E3467846e35749ae30b00ef0182",
            "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
            "decimals": 6
          },
          "bitcoinswap": {
            "router": "0x10EDFF80C17A39940a4dc724b9c24fE9D2E8Fd9b",
            "token": "0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727",
            "decimals": 18,
            "granularity": "1000000000000"
          }
        },
        {
          "name": "USD₮0",
          "arbitrum": {
            "router": "0x984fDC24D1A2Be538C7290487d0fc212635D98A5",
            "token": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
            "decimals": 6
          },
          "bitcoinswap": {
            "router": "0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1",
            "token": "0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727",
            "decimals": 18,
            "granularity": "1000000000000"
          }
        }
      ],
      "routerAbi": [
        "function transferRemote(uint32 destination, bytes32 recipient, uint256 amount) payable returns (bytes32 messageId)",
        "function wrappedToken() view returns (address)",
        "function granularity() view returns (uint256)",
        "function allowlistEnabled() view returns (bool)",
        "function isAllowed(address) view returns (bool)",
        "function paused() view returns (bool)",
        "function routers(uint32 domain) view returns (bytes32)",
        "function mailbox() view returns (address)",
        "event SentTransferRemote(uint32 indexed destination, bytes32 indexed recipient, uint256 amountOrId)",
        "event ReceivedTransferRemote(uint32 indexed origin, bytes32 indexed recipient, uint256 amountOrId)",
        "error NotAllowed(address account)",
        "error Paused()",
        "error BadGranularity(uint256 amount, uint256 granularity)"
      ],
      "ui": "https://btcw.tech/swap/#bridge"
    }
  },
  "links": {
    "swap": "https://btcw.tech/swap/",
    "bridge": "https://btcw.tech/swap/#bridge",
    "dashboard": "https://btcw.tech/dashboard/",
    "transparency": "https://btcw.tech/transparency.html",
    "docs": "https://btcw.tech/developers/",
    "swapApiDocs": "https://btcw.tech/developers/guides/wallet-swap/"
  }
}
