{"built":"20260930h","pages":[{"url":"/developers/","title":"Overview","group":"Get started","heads":[{"id":"live-status","t":"Live status"},{"id":"what-do-you-want-to-build","t":"What do you want to build?"},{"id":"machine-readable-resources","t":"Machine-readable resources"},{"id":"how-these-docs-are-organized","t":"How these docs are organized"}],"text":"Integrate Bitcoin Swap (chain 482120) into a wallet, bot or app: prices, swaps and the Arbitrum ⇄ 482120 bridge. Bitcoin Swap is an EVM chain (Hyperledger Besu, QBFT consensus, chain ID 482120 ) with three things you can integrate: prices from the on-chain oracle, swaps across 201 pools (100 crypto tokens, 100 US stocks, USDw) and OracleAMM, and a Hyperlane bridge to Arbitrum One. Every contract is verified on the explorer, and every public API works without a key. Live status Block 482120 … BTCw price (oracle) … Bridge — 482120 side … Bridge — Arbitrum side … Read directly from the chain and the API when you open this page. More detail on Status . What do you want to build? Show the price of BTCw or a …w token REST /api/price , or read the Chainlink-compatible feed on-chain. Let your wallet users swap Call one quote endpoint and get back a ready-to-sign transaction. Swap from a bot or contract Call ClpPools / OracleAMM directly — no server in between. Move funds between Arbitrum and 482120 USDC / USD₮0 ⇄ USDw 1 : 1 over a Hyperlane warp route. Machine-readable resources Resource Use /developers/contracts.json addresses + ABIs of every public contract, on both chains /chain.json network info in ethereum-lists format /api/v2/swap/assets list of 202 assets: address, symbol, logo, oracle price /swap/sdk/btcw-swap.js JavaScript SDK (ES module, no dependencies) /developers/search.json full-text index of these docs How these docs are organized Concepts explain how the system works; Guides walk through one specific task step by step; Reference lists every endpoint, function and error code. If you only have five minutes, read the Quickstart . Before shipping anything to real users, read Trust model & risks . There is no separate testnet. To experiment without spending real funds, run a chain fork locally — see Test on a chain fork ."},{"url":"/developers/quickstart/","title":"Quickstart","group":"Get started","heads":[{"id":"1-read-the-btcw-price","t":"1. Read the BTCw price"},{"id":"2-list-what-you-can-swap","t":"2. List what you can swap"},{"id":"3-get-a-quote","t":"3. Get a quote"},{"id":"4-or-ask-the-contract-directly","t":"4. Or ask the contract directly"},{"id":"5-send-a-transaction","t":"5. Send a transaction"}],"text":"From zero to your first swap quote in five minutes — just curl or a few lines of JavaScript. Steps 1–4 only read data — no wallet, no fees. Step 5 sends a real transaction. 1. Read the BTCw price curl -s https://btcw.tech/api/price Returns price_usd , OracleAMM's actual buy/sell prices ( bid_usd / ask_usd ) and the price age. Always check stale : once the price is older than max_age_seconds (300), OracleAMM stops filling trades. See Read prices . 2. List what you can swap curl -s https://btcw.tech/api/v2/swap/assets 202 assets: native BTCw (placeholder address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ), USDw and 200 price-tracker tokens (100 crypto, 100 US stocks). Every asset has 18 decimals. 3. Get a quote # swap 0.001 BTCw for USDw — amount is an integer in base units (18 decimals) curl -s \"https://btcw.tech/api/v2/swap/quote?from=native&to=0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727&amount=1000000000000000\" The response includes amountOut , minAmountOut (after 1% slippage), fees and the route. Add &sender=0xYourWallet to also receive a ready-to-sign tx . Details: Wallet swap integration . 4. Or ask the contract directly If you don't want to trust any server, eth_call OracleAMM: import { JsonRpcProvider, Contract, parseEther, formatEther } from \"ethers\"; const p = new JsonRpcProvider(\"https://rpc.btcw.tech\", 482120); const amm = new Contract(\"0xd953d53414a81eb6AC5a4edE73d13992181aF51E\", [\"function quoteSellBtcw(uint256) view returns (uint256)\"], p); console.log(formatEther(await amm.quoteSellBtcw(parseEther(\"0.001\"))), \"USDw\"); cast call 0xd953d53414a81eb6AC5a4edE73d13992181aF51E \"quoteSellBtcw(uint256)(uint256)\" 1000000000000000 --rpc-url https://rpc.btcw.tech 5. Send a transaction Add the network to your wallet — parameters on Network , or use the \"Add network\" button on the home page . Hold a little BTCw for gas (minimum gas price 0.001 gwei — a swap costs less than 0.000001 BTCw). Send the tx from step 3 via eth_sendTransaction , or follow Swap via contracts . To try step 5 without spending real funds: anvil --fork-url https://rpc.btcw.tech gives you a chain fork with 10 accounts, each holding 10,000 BTCw. See Test on a chain fork ."},{"url":"/developers/network/","title":"Network","group":"Get started","heads":[{"id":"parameters","t":"Parameters"},{"id":"gas-fees","t":"Gas fees"},{"id":"add-the-network-to-a-wallet","t":"Add the network to a wallet"},{"id":"add-the-usdw-token","t":"Add the USDw token"},{"id":"arbitrum-one","t":"Arbitrum One"}],"text":"Chain ID, RPC, WebSocket, explorer, gas and how to add the network to a wallet. Parameters Network name Bitcoin Swap Chain ID 482120 ( 0x75b48 ) — also the chain's Hyperlane domain RPC (HTTPS) https://rpc.btcw.tech — JSON-RPC over POST, batch requests supported RPC (WebSocket) wss://rpc.btcw.tech/ws — supports eth_subscribe ( newHeads , logs ) Explorer explorer.btcw.tech — Blockscout, with the /api/v2 API Native coin BTCw, 18 decimals Client · consensus Hyperledger Besu · QBFT Block time 5 seconds Finality QBFT finalizes as soon as a block is produced — no reorgs, no need to wait for extra confirmations Block gas limit 30,000,000 Gas fees The chain has EIP-1559 enabled, but baseFeePerGas is always 0. Nodes only accept transactions with a gas price of at least 1000000 wei (0.001 gwei). eth_gasPrice returns exactly this value; eth_maxPriorityFeePerGas returns 0 — wallets that compute EIP-1559 fees themselves should set both maxFeePerGas and maxPriorityFeePerGas to at least 1000000 . Example: a swap uses about 150,000 gas × 0.001 gwei ≈ 0.00000015 BTCw . Add the network to a wallet EIP-3085 — the same parameters the SDK exports as ADD_CHAIN_PARAMS : await window.ethereum.request({ method: \"wallet_addEthereumChain\", params: [{ chainId: \"0x75b48\", chainName: \"Bitcoin Swap\", nativeCurrency: { name: \"Bitcoin Swap\", symbol: \"BTCw\", decimals: 18 }, rpcUrls: [\"https://rpc.btcw.tech\"], blockExplorerUrls: [\"https://explorer.btcw.tech\"], iconUrls: [\"https://btcw.tech/logo.svg\"], }], }); ethereum-lists format: /chain.json . Add the USDw token await window.ethereum.request({ method: \"wallet_watchAsset\", params: { type: \"ERC20\", options: { address: \"0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727\", symbol: \"USDw\", decimals: 18, image: \"https://btcw.tech/usdw.png\" } }, }); Addresses and logos of the 200 price-tracker tokens are in /api/v2/swap/assets (the logoURI field). Arbitrum One The other end of the bridge: chain ID 42161 (also its Hyperlane domain), public RPC https://arb1.arbitrum.io/rpc , explorer arbiscan.io . See Bridge ."},{"url":"/developers/addresses/","title":"Contract addresses","group":"Get started","heads":[{"id":"assets","t":"Assets"},{"id":"swap","t":"Swap"},{"id":"oracle","t":"Oracle"},{"id":"bridge","t":"Bridge"},{"id":"owner-and-treasury-wallets","t":"Owner and Treasury wallets"}],"text":"Every live contract, grouped by function. Machine-readable: contracts.json. All contracts on the 482120 side are verified on explorer.btcw.tech . Arbitrum side: exact match on Sourcify / arbitrum.blockscout.com. Machine-readable version (addresses + ABIs): /developers/contracts.json . Always identify assets by chainId + address . Symbols such as BTCW and USDW are already used by unrelated assets on other chains. Assets Name Address (482120) Notes BTCw native coin — the API uses 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE pays gas; paired in every pool USDw 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 ERC-20, 18 decimals; the asset of the bridge and OracleAMM TokenFactory 0xa4860BdfaE9B2881B1B593aFFC8740d926eCC1d1 creates the 200 price-tracker tokens (100 crypto, 100 US stocks); token list: /api/v2/swap/assets Swap Name Address (482120) Notes ClpPools 0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0 201 THORChain-style pools, each paired with BTCw OracleAMM 0xd953d53414a81eb6AC5a4edE73d13992181aF51E BTCw ⇄ USDw at the oracle price ± 0.3% OracleTokenAMM (ETHw) 0xFd6d78A3b60175D37146793396A2FA7864039D54 ETHw ⇄ USDw at the oracle price ± 0.3%; the bridge's ETHw destination SwapRouter 0x397E607eA05eF7909f84540077F3a716778031E4 chains up to 4 hops across ClpPools / OracleAMM / the ETHw vault into one transaction; ownerless OracleRebalancer 0x6c811003D301769575e9A8661B9197FAf2A32e69 pulls token pool prices back to the oracle (crypto and stocks); minter of all 200 price-tracker tokens Oracle Name Address (482120) Notes BtcwPriceFeed 0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c BTC/USD, Chainlink AggregatorV3-compatible, 8 decimals PriceHub 0xE87FDdCC0b668564a20B23F3bE1EE108C805ecb4 USD prices of the 200 price-tracker tokens, 18 decimals Bridge Route Arbitrum One (42161) Bitcoin Swap (482120) USDC router 0x10B5757aC9a40E3467846e35749ae30b00ef0182 token USDC 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 router 0x10EDFF80C17A39940a4dc724b9c24fE9D2E8Fd9b token USDw USD₮0 router 0x984fDC24D1A2Be538C7290487d0fc212635D98A5 token USD₮0 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 router 0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1 token USDw Mailbox 0xBf4E868aE56870DC92dC906E1ad21ce038EF3198 0x489DC54c2d2f7df9175d7F99716094C85E008392 Owner and Treasury wallets Published so you can filter or monitor them yourself: Role Address Owner — owns every contract above, on both chains 0x4aFd0A931176103d704Ff0d696614d4E10d74A4a Treasury arbitrage bot (USDw pool) 0x4b3418e3d21932610faafef8caefdb0f2c05a836 The Dashboard and /api/v2/stats report the volume of the bot and OracleRebalancer separately (the treasury_addresses field) — these are the trades that keep pool prices near the oracle. Owner permissions: Trust model ."},{"url":"/developers/assets/","title":"Assets","group":"Concepts","heads":[{"id":"btcw-native-coin","t":"BTCw — native coin"},{"id":"usdw","t":"USDw"},{"id":"price-tracker-tokens-w","t":"Price-tracker tokens (…w)"},{"id":"identifying-assets","t":"Identifying assets"},{"id":"logos","t":"Logos"}],"text":"BTCw, USDw and the 200 price-tracker tokens (crypto and US stocks): what they are and how to identify them. The chain has 202 tradable assets. All of them use 18 decimals . BTCw — native coin The chain's native coin, used to pay gas and as the shared side of every pool. In the API and SDK, BTCw is represented by the placeholder address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE (the strings native , BTCw and 0x000…0 are also accepted). Price = BTC/USD rate from the oracle × OracleAMM's peg ratio ratioWad (currently 1). USDw ERC-20 at 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 . Used as the unit of account in OracleAMM and as the bridge asset. The Owner can mint USDw ( mint is Owner-only). The bridge moves USDw 1 : 1 against USDC / USD₮0 locked in the Arbitrum routers; withdrawals are capped at the amount locked on that side. Current total supply: the usdw_total_supply field of /api/price . Price-tracker tokens (…w) 200 tokens in two classes. Each token tracks the USD price of an external asset via the PriceHub oracle and has one pool against BTCw in ClpPools. The class field of /api/v2/pools gives the class: crypto , stock or stable (USDw). Class Count Examples Tracks Crypto 100 ETHw, SOLw, BNBw top 100 tokens by market cap US stocks 100 NVDAw, AAPLw, MSFTw the share price of the 100 largest US companies by market cap; named \"<Company> Stock Price Tracker\" Both classes run on the same mechanism: the same TokenFactory, PriceHub and ClpPools, and the same OracleRebalancer + keeper keeping pool prices near the oracle. The tracked price is the regular-session share price, multiplied by a split factor ( splitFactor ) so the series stays continuous across stock splits. Tokens are created by TokenFactory ; the only minter is OracleRebalancer , within each token's mint cap and a daily mint-value cap (see Swap mechanics ). Full list (address, name, logo, oracle price, pool depth): /api/v2/swap/assets . Identifying assets Always match on chainId 482120 + contract address . Do not match on symbol: BTCW , USDW and many …w symbols are already used by unrelated assets on other chains and on price-data platforms. Symbols are for display only. The quote API still accepts symbols for convenient manual testing, but production integrations must send addresses. Logos BTCw logo: https://btcw.tech/logo.svg (a logo.png version is also available). USDw: https://btcw.tech/usdw.png . Price-tracker tokens: the logoURI field in /api/v2/swap/assets ."},{"url":"/developers/swap/","title":"Swap mechanics","group":"Concepts","heads":[{"id":"clppools-201-thorchain-style-pools","t":"ClpPools — 201 THORChain-style pools"},{"id":"oracleamm-btcw-usdw-at-the-oracle-price","t":"OracleAMM — BTCw ⇄ USDw at the oracle price"},{"id":"ethw-oracle-priced-vault","t":"ETHw oracle-priced vault"},{"id":"route-selection","t":"Route selection"},{"id":"how-pool-prices-are-pulled-back-to-the-oracle","t":"How pool prices are pulled back to the oracle"},{"id":"stock-pools-outside-market-hours","t":"Stock pools outside market hours"},{"id":"providing-liquidity","t":"Providing liquidity"}],"text":"THORChain-style CLP pools, oracle-priced AMMs and how pool prices are pulled back to the oracle. There are two execution venues. The Swap page, the quote API and the SDK query both and pick the one that gives the user more. ClpPools — 201 THORChain-style pools Each pool pairs one asset (USDw, a crypto price-tracker token or a stock price-tracker token) with BTCw. It uses THORChain's CLP (continuous liquidity pool) formula: y = x · X · Y / (x + X)² x : amount in X : input-side depth y : amount out Y : output-side depth The difference from the mid price x · Y / X is the slip fee: the larger the trade relative to pool depth, the higher the fee. The minimum fee minFeeBps is 5 bps (0.05%) — every trade pays at least this, however small. Fees stay in the pool. Token → token takes two hops through BTCw ( swapAssetForAsset ) in a single transaction and pays fees in both pools. Recipient is optional (the to parameter) — you can pay out directly to another wallet. Every swap function takes minOut ; if the price moves too far it reverts with SlippageExceeded . OracleAMM — BTCw ⇄ USDw at the oracle price No curve: trades fill at the BTC/USD oracle price ± spreadBps . sell BTCw : USDw out = x · p · (1 − spread) buy BTCw : BTCw out = u / p · (1 − spread) Parameter Current Meaning spreadBps 30 (0.3%) fixed spread in each direction maxTradeBtcw 10 BTCw maximum per trade (in BTCw terms) maxAgeSec 300 seconds rejects trades when the oracle price is older ( StalePrice ) OracleAMM only pays the sender. To pay another wallet, use the ClpPools route. OracleAMM liquidity is deposited and withdrawn by the Owner. ETHw oracle-priced vault Address 0xFd6d78A3b60175D37146793396A2FA7864039D54 ( OracleTokenAMM ). Works like OracleAMM but for ETHw ⇄ USDw, priced at the PriceHub price of ETHw ± spreadBps : Parameter Current Meaning spreadBps 30 (0.3%) fixed spread in each direction maxTradeToken 300 ETHw maximum per trade maxAgeSec 300 seconds rejects trades when the price is older ( StalePrice ) The vault was seeded by the Owner with 12,280 ETHw + 33,100,000 USDw. buyToken / sellToken take a to parameter — you can pay out directly to another wallet. The Bridge page uses this vault when the user chooses to receive or send ETHw. Route selection For the BTCw ⇄ USDw pair, the quote queries both the CLP pool and OracleAMM and picks the larger output ( route=best ). You can force one venue with route=clp or route=amm . All other pairs use ClpPools only. How pool prices are pulled back to the oracle Price-tracker tokens have no external market, so pool prices would drift away from the oracle if nothing pulled them back. Two mechanisms do that: OracleRebalancer (200 token pools — 100 crypto and 100 US stocks, same mechanism): when a pool deviates from the oracle by more than 30 bps , the contract trades to close 90% of the gap, at most 5 BTCw per trade. Pool above oracle → it sells tokens it holds, minting more if short; pool below oracle → it buys with the contract's BTCw. Safeguards: no action when the oracle price is older than 300 seconds; a mint-value cap of 300 BTCw per day (shared across all 200 tokens); a cumulative mint cap per token; pausable. The keeper reads the pool list straight from ClpPools every round and calls the Rebalancer in batches of 25 tokens — new pools are picked up automatically. Treasury bot (USDw pool): trades to pull the USDw pool back to the oracle price. Stock pools outside market hours Outside US regular trading hours and on weekends, the oracle holds the closing price , and the Rebalancer keeps pools near that price. Stock pools remain tradable 24/7; when BTC moves, the BTCw-denominated price of stock pools drifts too and is pulled back just like crypto pools. At market open, if the opening price is far from the close, the keeper moves the pool gradually (at most 5 BTCw per trade). A price update that moves more than 10% is blocked by the oracle — that symbol stays frozen until the Owner confirms the new price ( ownerSubmit ). What this means for integrators: pool prices usually sit within a few dozen bps of the oracle price. The volume of these two wallets is reported separately on the Dashboard and in /api/v2/stats ( of_which_treasury ). Providing liquidity Pool liquidity is currently provided by the Owner. These docs do not cover third-party liquidity provision."},{"url":"/developers/oracle/","title":"Price oracle","group":"Concepts","heads":[{"id":"btc-usd-btcwpricefeed","t":"BTC/USD — BtcwPriceFeed"},{"id":"200-price-tracker-tokens-pricehub","t":"200 price-tracker tokens — PriceHub"},{"id":"staleness-and-when-to-stop","t":"Staleness and when to stop"},{"id":"oracle-risks","t":"Oracle risks"}],"text":"How BTC/USD and the prices of the 200 tracker tokens (crypto and US stocks) are sourced, written and protected. BTC/USD — BtcwPriceFeed Contract 0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c , Chainlink AggregatorV3Interface -compatible, 8 decimals . The feeder takes the median of 6 sources : Chainlink BSC, Chainlink ETH, Binance, Coinbase, Kraken, OKX. At least 3 sources must be live. It writes on-chain when the price deviates by at least 0.05% from the on-chain price, or at most every 30 seconds even if the price is unchanged. Price-jump guard : no single update may move the price more than 10% from the previous one ( PriceJumpTooLarge ) — even a compromised feeder key cannot move the price far in one write. Only the updater address can write ( NotUpdater ); the Owner has ownerSubmit for manual writes and can change the updater and the jump limit. BtcwPriceFeed provides the BTC/USD price. BTCw price = this price × OracleAMM's peg ratio ratioWad (currently 1). 200 price-tracker tokens — PriceHub Contract 0xE87FDdCC0b668564a20B23F3bE1EE108C805ecb4 , 18 decimals . Each token's price is the median across multiple sources (at least 3, discarding any source more than 2% from the median) — if one source goes down, others remain. Each class has its own source set: Crypto (100): multiple major crypto exchanges. US stocks (100): 5 public quote sources — Robinhood, Yahoo Finance, CNBC, TradingView, Nasdaq. These are unofficial public APIs of those sites and may change or be blocked. The price is the regular-session price × splitFactor ; outside market hours and on weekends it is the latest close. The feeder only prices tokens on each class's list — unknown tokens in the factory are skipped, never guessed from their symbol. It writes when the price deviates by at least 0.1% , or at most every 120 seconds . There is also a per-write jump guard; a blocked write emits a Rejected event instead of reverting the whole batch. 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) Full signatures: Contract reference . Staleness and when to stop OracleAMM and OracleRebalancer stop trading when the price is older than 300 seconds . Your application should do the same: REST: check stale and age_seconds in /api/price ; for tokens, priceAgeS in /api/v2/pools . On-chain: compare updatedAt with the block timestamp; do not use a price that is too old. If the feeder stops (server failure, all sources failing at once), the price freezes — nothing on-chain updates it automatically. Oracle risks The 10%-per-write jump guard limits speed, not direction: several consecutive writes can still move the price far. The Owner can change the updater and the jump limit, with no delay."},{"url":"/developers/bridge/","title":"Bridge","group":"Concepts","heads":[{"id":"model","t":"Model"},{"id":"lifecycle-of-a-transfer","t":"Lifecycle of a transfer"},{"id":"dedicated-hyperlane-infrastructure","t":"Dedicated Hyperlane infrastructure"},{"id":"rounding-granularity","t":"Rounding — granularity"},{"id":"limits-and-pausing","t":"Limits and pausing"},{"id":"receive-btcw-or-ethw-directly","t":"Receive BTCw or ETHw directly"}],"text":"Hyperlane collateral ⇄ collateral warp routes between Arbitrum One and Bitcoin Swap. Model Hyperlane warp route of type collateral ⇄ collateral ( @hyperlane-xyz/core 12.1.0), 1 : 1 ratio, two routes: Route Arbitrum One Bitcoin Swap 482120 USDC USDC (6 decimals) locked in the router ⇄ USDw (18 decimals) released from the router's vault USD₮0 USD₮0 (6 decimals) locked in the router ⇄ USDw (18 decimals) released from the router's vault Nothing is minted on either side: each router only releases what it currently holds. So withdrawals from one side can never exceed the amount locked on that side . Both routes share USDw on the 482120 side but have separate vaults. Lifecycle of a transfer The user calls transferRemote on the source-chain router — tokens are locked in the router and the Mailbox dispatches a message. The source chain's validator signs a checkpoint for the message. The relayer delivers the message + signature to the destination Mailbox. The ISM (Interchain Security Module) on the destination chain verifies the signature: multisig message-id with a 1/1 threshold. The destination router releases tokens to the recipient and emits ReceivedTransferRemote . Usually takes 10–30 seconds . If the relayer is down, messages wait and are delivered once it restarts — sent messages are not lost. Dedicated Hyperlane infrastructure The Mailbox, hooks and ISM on both chains are dedicated deployments for Bitcoin Swap, not the standard Mailbox of the Hyperlane network. The bridge's validator and relayer are dedicated as well. Consequences: Hyperlane Explorer and public relayers do not track or deliver these messages. There is no interchain gas payment: call transferRemote with msg.value = 0 ; the bridge relayer pays destination gas itself. Bridge security = security of the validator key and the Owner key. See Trust model . Rounding — granularity USDC/USD₮0 have 6 decimals, USDw has 18. The 482120 router only accepts amounts divisible by granularity() = 10 12 (i.e. rounded to 6 decimal places); finer amounts revert with BadGranularity . Limits and pausing The routers are GuardedCollateralRouter — a standard warp route plus two Owner-controlled switches: allowlistEnabled() : when on, only allowlisted wallets can send ( NotAllowed ). Currently off on all 4 routers — any wallet can send. paused() : halts sending ( Paused ). In-flight messages are not lost and are delivered after unpausing. Each chain also has a PausableHook for emergency stops at the Mailbox level. Live state: Status . Step by step: Bridge funds . Receive BTCw or ETHw directly The btcw.tech/swap/#bridge page lets you pick the asset on the Bitcoin Swap side: USDw (1 : 1), BTCw (swapped via OracleAMM) or ETHw (swapped via the ETHw oracle-priced vault). On deposit, USDw is then swapped to BTCw/ETHw (one more transaction on 482120, which needs a little BTCw for gas); on withdrawal, BTCw/ETHw is first swapped to USDw and then bridged. The bridge itself only moves USDw."},{"url":"/developers/trust/","title":"Trust model","group":"Concepts","heads":[{"id":"summary","t":"Summary"},{"id":"owner-permissions-by-contract","t":"Owner permissions by contract"},{"id":"integration-risks","t":"Integration risks"},{"id":"what-you-should-do","t":"What you should do"}],"text":"Who controls what and what they can do — know this before you integrate. This page lists who holds which permissions on each contract, so you can decide how much trust is appropriate before onboarding users. Summary A single Owner wallet ( 0x4aFd0A931176103d704Ff0d696614d4E10d74A4a ) owns every contract on both chains. No multisig, no timelock: every change takes effect in the next block. Owner permissions by contract Contract Owner can USDw mint without limit; change name/symbol TokenFactory · price-tracker tokens change the minter, change mint caps, change name/symbol, transfer token ownership OracleRebalancer change parameters (threshold, step, daily cap…), set the keeper, pause, withdraw the contract's assets ClpPools create pools, enable/disable individual pools, change the minimum fee (up to 10%), pause; withdraw Owner-provided liquidity. sweep only recovers funds sent by mistake — it cannot withdraw pool depth OracleAMM deposit/withdraw liquidity, change spread / per-trade cap / max price age, change the price feed, change the peg ratio, pause BtcwPriceFeed · PriceHub write prices manually ( ownerSubmit ), change the updater, change the price-jump limit Bridge routers (4) pause, enable the allowlist, and every permission of a Hyperlane warp route: change the ISM, hook, remote routers — meaning it is technically possible to allow vault withdrawals without a matching deposit All of these permissions can be read in the verified source code on the explorer. We are not aware of any permission outside the list above. Integration risks Code: BETA — not independently audited. Internal testing and rehearsals on a fork of the live chain are no substitute for an audit. What you should do Read paused() , allowlistEnabled() and price age before every action — don't hardcode them. Monitor parameter-change events ( ParamsSet , PausedSet , UpdaterSet , MinFeeSet , AllowlistEnabledSet …) if you hold user funds. Cap the amount users deposit at what you are prepared to lose. Self-verifiable figures (genesis, block cadence): Transparency page ."},{"url":"/developers/guides/read-prices/","title":"Read prices","group":"Guides","heads":[{"id":"option-1-rest-the-simplest","t":"Option 1 — REST, the simplest"},{"id":"prices-of-the-200-price-tracker-tokens-crypto-an","t":"Prices of the 200 price-tracker tokens (crypto and US stocks)"},{"id":"option-2-on-chain-trust-no-server","t":"Option 2 — on-chain, trust no server"},{"id":"from-a-solidity-contract-on-chain-482120","t":"From a Solidity contract on chain 482120"},{"id":"price-tracker-token-prices-pricehub","t":"Price-tracker token prices — PriceHub"},{"id":"watch-for-price-changes","t":"Watch for price changes"}],"text":"Get BTCw and tracker-token prices over REST or on-chain (Chainlink AggregatorV3 standard). Option 1 — REST, the simplest curl -s https://btcw.tech/api/price { \"symbol\": \"BTCw\", \"chain_id\": 482120, \"decimals\": 18, \"price_usd\": 83380.39, // oracle price × peg ratio \"bid_usd\": 83130.25, // OracleAMM buys your BTCw at this price \"ask_usd\": 83630.53, // OracleAMM sells you BTCw at this price \"spread_pct\": 0.3, \"updated_at\": 1790739823, \"age_seconds\": 5, \"max_age_seconds\": 300, \"stale\": false, \"paused\": false, \"tradable\": true, \"liquidity\": { \"btcw\": 1001, \"usdw\": 49916961.49, \"max_trade_btcw\": 10 }, \"oracle\": { \"address\": \"0x6E06…a96c\", \"decimals\": 8, \"sources\": \"median of 6: …\" }, \"usdw_total_supply\": 122171970.27, … } Safe to use when stale == false and paused == false . tradable combines both conditions with liquidity. Prices of the 200 price-tracker tokens (crypto and US stocks) curl -s https://btcw.tech/api/v2/pools # per pool: oracleUsd, poolUsd, devBps, depthBtcw, priceAgeS… curl -s https://btcw.tech/api/v2/swap/assets # 202 assets: address, symbol, decimals, logoURI, priceUsd Filter by class with the class field ( crypto · stock · stable ). oracleUsd is the oracle price (PriceHub); poolUsd is the current pool price; devBps is the deviation between the two. Staleness: check priceAgeS (a sensible threshold: 300 seconds). Responses are cached for 5 seconds ( cache-control: public, max-age=5 ) — polling more often won't return fresher data. Rate limit: 20 requests/second per IP. Option 2 — on-chain, trust no server BtcwPriceFeed follows the Chainlink interface, so any tool that reads Chainlink feeds can read it: cast call 0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c \\ \"latestRoundData()(uint80,int256,uint256,uint256,uint80)\" --rpc-url https://rpc.btcw.tech import { JsonRpcProvider, Contract } from \"ethers\"; const p = new JsonRpcProvider(\"https://rpc.btcw.tech\", 482120); const feed = new Contract(\"0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c\", [\"function latestRoundData() view returns (uint80,int256,uint256,uint256,uint80)\"], p); const [, answer, , updatedAt] = await feed.latestRoundData(); const age = Math.floor(Date.now() / 1000) - Number(updatedAt); if (age > 300) throw new Error(\"oracle price is stale\"); console.log(\"BTC/USD\", Number(answer) / 1e8); From a Solidity contract on chain 482120 interface AggregatorV3Interface { function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); } (, int256 answer,, uint256 updatedAt,) = AggregatorV3Interface(0x6E0687A5D5a5b98CC5fb05c4d8d742fBD75ba96c).latestRoundData(); require(block.timestamp - updatedAt <= 300, \"stale\"); // 8 decimals Price-tracker token prices — PriceHub # ETHw price (18 decimals) — substitute a token address from /api/v2/swap/assets cast call 0xE87FDdCC0b668564a20B23F3bE1EE108C805ecb4 \\ \"latestRoundData(address)(uint80,int256,uint256,uint256,uint80)\" <token-address> --rpc-url https://rpc.btcw.tech Several tokens at once: prices(address[]) returns two arrays, answers and updatedAts ; tokens without a price return (0, 0) (whereas latestRoundData reverts with NoData ). Watch for price changes Instead of polling, subscribe to events over WebSocket at wss://rpc.btcw.tech/ws : // BtcwPriceFeed: AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt) // PriceHub: AnswerUpdated(address indexed asset, int256 answer, uint256 roundId, uint256 updatedAt) const ws = new WebSocketProvider(\"wss://rpc.btcw.tech/ws\", 482120); feed.connect(ws).on(\"AnswerUpdated\", (current) => console.log(Number(current) / 1e8));"},{"url":"/developers/guides/swap-contracts/","title":"Swap through contracts","group":"Guides","heads":[{"id":"general-flow","t":"General flow"},{"id":"oracleamm-btcw-usdw","t":"OracleAMM — BTCw ⇄ USDw"},{"id":"clppools-every-pair","t":"ClpPools — every pair"},{"id":"with-foundry-cast","t":"With Foundry cast"},{"id":"multi-hop-in-one-transaction-swaprouter","t":"Multi-hop in one transaction — SwapRouter"},{"id":"tracking-results","t":"Tracking results"}],"text":"Quote and send swaps with ethers v6 or Foundry cast, without any server in between. 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 with SlippageExceeded , costing nothing but gas. If selling an ERC-20 token: approve the 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: approve the exact amount to the router — the router only pulls from msg.sender . quote does not check per-trade caps, inventory or pause state — the SDK and the quote API check those beforehand. deadline is a Unix timestamp; past it, the call reverts with Expired . # 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 ."},{"url":"/developers/guides/wallet-swap/","title":"Swaps for wallets","group":"Guides","heads":[{"id":"asset-list","t":"Asset list"},{"id":"quote-get-api-v2-swap-quote","t":"Quote — GET /api/v2/swap/quote"},{"id":"sdk","t":"SDK"},{"id":"deep-link","t":"Deep link"}],"text":"A THORChain-style quote API that returns ready-to-sign transactions — any pair of the 202 assets. 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 ."},{"url":"/developers/guides/bridge/","title":"Bridge funds","group":"Guides","heads":[{"id":"pre-send-checks","t":"Pre-send checks"},{"id":"steps","t":"Steps"},{"id":"withdraw-482120-arbitrum","t":"Withdraw: 482120 → Arbitrum"},{"id":"deposit-arbitrum-482120","t":"Deposit: Arbitrum → 482120"},{"id":"with-ethers-v6","t":"With ethers v6"},{"id":"warp-route-config-hyperlane-registry-format","t":"Warp route config (Hyperlane registry format)"}],"text":"Deposit USDC / USD₮0 from Arbitrum as USDw on 482120, and withdraw back. End users should use the UI at btcw.tech/swap/#bridge . This page is for bots, wallets and apps that call the contracts directly. How it works: Bridge . Pre-send checks paused() on the source router must be false (otherwise: revert Paused() ). allowlistEnabled() : if true , isAllowed(wallet) must also be true (otherwise: NotAllowed(address) ). Currently disabled. Destination liquidity : the destination router must hold enough tokens. Withdrawing to Arbitrum: the USDC/USD₮0 balance of that route's Arbitrum router. Depositing to 482120: the USDw balance of the 482120 router. If it is short, the message arrives but cannot be delivered. From 482120: the amount must be divisible by granularity() = 10 12 . # destination liquidity when withdrawing USD₮0 to Arbitrum cast call 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9 \"balanceOf(address)(uint256)\" 0x984fDC24D1A2Be538C7290487d0fc212635D98A5 --rpc-url https://arb1.arbitrum.io/rpc # destination liquidity when depositing USD₮0 to 482120 cast call 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 \"balanceOf(address)(uint256)\" 0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1 --rpc-url https://rpc.btcw.tech Steps approve(router, amount) on the source-chain token — the exact amount. transferRemote(uint32 destination, bytes32 recipient, uint256 amount) on the source-chain router, msg.value = 0 . destination : 482120 to deposit, 42161 to withdraw. recipient : destination wallet address left-padded with zeros to 32 bytes (Hyperlane standard). Wait 10–30 seconds and watch for ReceivedTransferRemote on the destination router. Pad on the left : cast abi-encode \"f(address)\" 0xABC… produces the right format. Do not use cast to-bytes32 — it pads on the right, and the funds go to an address nobody holds the key for. Withdraw: 482120 → Arbitrum # withdraw 1 USDw as USD₮0 to wallet 0xABC… on Arbitrum RECIPIENT=$(cast abi-encode \"f(address)\" 0xABC...) cast send 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 \"approve(address,uint256)\" \\ 0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1 1000000000000000000 --rpc-url https://rpc.btcw.tech --private-key $PK cast send 0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1 \"transferRemote(uint32,bytes32,uint256)\" \\ 42161 $RECIPIENT 1000000000000000000 --rpc-url https://rpc.btcw.tech --private-key $PK 1 USDw (18 decimals) → 1 USD₮0 (6 decimals) received on Arbitrum. Deposit: Arbitrum → 482120 # deposit 5 USDC (6 decimals) → 5 USDw to wallet 0xABC… on 482120 RECIPIENT=$(cast abi-encode \"f(address)\" 0xABC...) cast send 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 \"approve(address,uint256)\" \\ 0x10B5757aC9a40E3467846e35749ae30b00ef0182 5000000 --rpc-url https://arb1.arbitrum.io/rpc --private-key $PK cast send 0x10B5757aC9a40E3467846e35749ae30b00ef0182 \"transferRemote(uint32,bytes32,uint256)\" \\ 482120 $RECIPIENT 5000000 --rpc-url https://arb1.arbitrum.io/rpc --private-key $PK The receiving wallet needs a little BTCw to do anything further on 482120 — the bridge only moves USDw. With ethers v6 import { Contract, AbiCoder, parseUnits } from \"ethers\"; const router = new Contract(\"0x10B5757aC9a40E3467846e35749ae30b00ef0182\", [ \"function transferRemote(uint32,bytes32,uint256) payable returns (bytes32)\", \"function paused() view returns (bool)\", ], signer); const usdc = new Contract(\"0xaf88d065e77c8cC2239327C5EDb3A432268e5831\", [\"function approve(address,uint256) returns (bool)\"], signer); const amount = parseUnits(\"5\", 6); const recipient = AbiCoder.defaultAbiCoder().encode([\"address\"], [await signer.getAddress()]); // left-padded to 32 bytes if (await router.paused()) throw new Error(\"bridge is paused\"); await (await usdc.approve(router.target, amount)).wait(); const tx = await router.transferRemote(482120, recipient, amount, { value: 0 }); const receipt = await tx.wait(); // watch ReceivedTransferRemote on the 482120 router to know the funds arrived Warp route config (Hyperlane registry format) tokens: - chainName: arbitrum # domain 42161 standard: EvmHypCollateral addressOrDenom: \"0x984fDC24D1A2Be538C7290487d0fc212635D98A5\" collateralAddressOrDenom: \"0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9\" # USD₮0 decimals: 6 connections: [{ token: \"ethereum|bitcoinswap|0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1\" }] - chainName: bitcoinswap # domain 482120 standard: EvmHypCollateral addressOrDenom: \"0xD6d8452FdCC26B155BEc4fc878635c05CaC95FB1\" collateralAddressOrDenom: \"0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727\" # USDw decimals: 18 connections: [{ token: \"ethereum|arbitrum|0x984fDC24D1A2Be538C7290487d0fc212635D98A5\" }] # the USDC route is identical: Arbitrum 0x10B5757a…0182 (USDC 0xaf88…5831) ⇄ 482120 0x10EDFF80…8Fd9b (USDw) The Mailbox and ISM are a dedicated deployment (see Bridge ), so this config is not in Hyperlane's public registry. Source code: 7 contracts on the 482120 side are verified on explorer.btcw.tech; 9 contracts on the Arbitrum side are exact matches on Sourcify / arbitrum.blockscout.com."},{"url":"/developers/guides/sandbox/","title":"Test on a chain fork","group":"Guides","heads":[{"id":"run","t":"Run"},{"id":"try-a-swap","t":"Try a swap"},{"id":"using-the-sdk-and-quote-api-with-a-fork","t":"Using the SDK and quote API with a fork"},{"id":"impersonate-any-wallet","t":"Impersonate any wallet"},{"id":"notes","t":"Notes"}],"text":"Run a copy of the live chain locally with anvil and test integrations without real funds. There is no separate testnet and no faucet. The safe way to experiment is to run a fork of the live chain on your own machine with anvil (Foundry): same contracts, same pools, same balances as mainnet at the block you choose — but every transaction stays on your machine. Run anvil --fork-url https://rpc.btcw.tech anvil serves RPC at http://127.0.0.1:8545 , keeps chain ID 482120 , and creates 10 test wallets, 10,000 BTCw each (private keys are printed to the console — local use only, never use them on mainnet). Pin the block so every run is identical: anvil --fork-url https://rpc.btcw.tech --fork-block-number 124000 Try a swap ME=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 # anvil test wallet #0 RPC=http://127.0.0.1:8545 cast call 0xd953d53414a81eb6AC5a4edE73d13992181aF51E \"quoteSellBtcw(uint256)(uint256)\" 1000000000000000 --rpc-url $RPC cast send 0xd953d53414a81eb6AC5a4edE73d13992181aF51E \"sellBtcw(uint256)\" 82000000000000000000 \\ --value 1000000000000000 --from $ME --unlocked --rpc-url $RPC cast call 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 \"balanceOf(address)(uint256)\" $ME --rpc-url $RPC These exact commands were run on 30 Sep 2026: sold 0.001 BTCw, received ~83 USDw. Adjust minUsdOut to the quote you get. Using the SDK and quote API with a fork The SDK accepts an optional RPC, so quotes and prebuilt transactions are read from your fork: import { getQuote } from \"./btcw-swap.js\"; // copy the file locally, see the SDK page const q = await getQuote({ from: \"native\", to: \"0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727\", amount: 10n ** 15n, sender: \"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\", rpc: \"http://127.0.0.1:8545\" }); The btcw.tech/api/v2/swap/quote API always reads mainnet — with a fork, use the SDK as above. Impersonate any wallet To test with the token balances of a real wallet (on the fork only): cast rpc anvil_impersonateAccount 0xWalletToImpersonate --rpc-url $RPC cast send … --from 0xWalletToImpersonate --unlocked --rpc-url $RPC Notes anvil reads state from rpc.btcw.tech on demand, so it is subject to the 10 requests/second per IP rate limit. If you hit 429 errors with a large test suite, lower --compute-units-per-second (default 330) so anvil throttles itself, e.g. --compute-units-per-second 150 . The oracle does not update on a fork: 300 seconds after the fork block, OracleAMM reports StalePrice . Restart anvil to fork from the latest block. The bridge does not work on a fork — there is no relayer. You can test approve + transferRemote , but not delivery. Foundry tests: forge test --fork-url https://rpc.btcw.tech runs Solidity tests against live state."},{"url":"/developers/api/","title":"REST API","group":"Reference","heads":[{"id":"conventions","t":"Conventions"},{"id":"prices","t":"Prices"},{"id":"swap","t":"Swap"},{"id":"stats","t":"Stats"},{"id":"health","t":"Health"},{"id":"static-files","t":"Static files"}],"text":"Every public endpoint: parameters, response fields, caching and examples. Conventions Base URL https://btcw.tech — the same API is served at https://app.btcw.tech Authentication no API key required Format JSON, UTF-8; GET only. CORS open ( Access-Control-Allow-Origin: * ) — callable directly from the browser Token amounts in the quote API ( /api/v2/swap/* ): integer strings, base units (18 decimals) — never parse them as floats. In the stats endpoints: floats, human units (BTCw, USD) Time /api/* and the ts , t , expiresAt fields: Unix seconds. The updated field of /api/v2/* : Unix milliseconds Caching per the cache-control header listed for each endpoint; polling faster than that returns no new data Rate limit 20 requests/second per IP, shared across /api/* , /api/v2/* and the explorer API — see Limits & error codes Prices all USD prices come from on-chain oracles (BtcwPriceFeed, PriceHub) Prices GET /api/price max-age=5 BTCw price and OracleAMM status. No parameters. Field Type Description price_usd number oracle BTC/USD price × peg ratio bid_usd / ask_usd number actual OracleAMM buy / sell price for BTCw (spread included) spread_pct number spread per side, % (0.3) updated_at , age_seconds , max_age_seconds number when the price was written, price age, staleness threshold (300) stale , paused , tradable bool price is stale; OracleAMM is paused; orders can be filled (not stale, not paused, liquidity available) liquidity object btcw , usdw held in OracleAMM; max_trade_btcw oracle object round , answer_btc_usd , decimals , address , sources contracts object amm , usdw , feed addresses usdw_total_supply , block number USDw total supply; block at read time curl -s https://btcw.tech/api/price GET /api/tokens max-age=5 BTCw and USDw in a compact wallet format: symbol , name , address ( null for native BTCw), native , decimals , price_usd , logo . The 200 price-tracker tokens are at /api/v2/swap/assets . GET /api/v2/pools max-age=5 The 201 ClpPools pools. Returns { updated, pools: [...] } . Field Description address , symbol , name , logo the pool's token (logo is a path relative to btcw.tech) class crypto · stock (US equities) · stable (USDw) oracleUsd / poolUsd / devBps oracle price, pool price, deviation (bps, positive = pool is more expensive) depthBtcw / depthAsset pool depth on each side tvlUsd , vol24hUsd , fees24hUsd , swaps24h , feeTvlPct24h stats, valued at the oracle price at indexing time; includes Treasury trades priceAgeS age of the token's oracle price, seconds Swap GET /api/v2/swap/assets max-age=30 The 202 swappable assets: { chainId, updated, assets: [{ address, native, symbol, name, decimals, logoURI, priceUsd, poolDepthBtcw }] } . Native BTCw has address = 0xEeee…EEeE and native: true . GET /api/v2/swap/quote no-store Quote + prebuilt transaction. Full guide and example response: Wallet swap integration . Parameter Description from , to required token address or native amount required integer, base units sender when set, returns tx , approval and simulates the transaction recipient receiver; if different from sender , only the ClpPools route remains slippage_bps 100 1–5000 → minAmountOut route best best · clp · amm (BTCw ⇄ USDw) · amm-eth (ETHw ⇄ USDw) · router (multi-hop through SwapRouter in one transaction) Status codes: 200 quote returned · 400 invalid input ( { error } ) · 422 no route can fill the order ( routes[].error still included) · 502 RPC read error. Stats GET /api/v2/stats max-age=5 Chain-wide figures used by the Dashboard: block , validators , btcw_usd , tvl_usd (split into tvl_clp_usd / tvl_oracleamm_usd ), volume_24h , swaps_24h , fees_24h_usd , total_swaps , wallets , oracle_assets_fresh (e.g. \"100/100\" ), network , status (per-component health), treasury_addresses . Volume includes of_which_treasury… fields — the share traded by the Treasury wallets ( treasury_addresses ) to keep pool prices close to the oracle. GET /api/v2/history?hours=48 max-age=5 Hourly series, hours up to 720. Each entry: t (start of hour), volClp , volAmm , volTreasury , swaps , swapsTreasury , fees (USD). GET /api/v2/swaps?limit=50 max-age=5 Latest swaps ( limit up to 300) and the 10 largest swaps in 24 hours: { latest, top24h } . Each swap: venue , from , to , amountIn , amountOut , usd , fee , trader , treasury (bool), block , tx , ts . Health GET /api/health max-age=5 Price API: { ok, block, age_seconds } . GET /api/v2/health max-age=5 Indexer: { ok, lastBlock, builtAgoS } . Returns 503 until indexing has finished (after a restart) — as do all other /api/v2/* endpoints. Static files Path Contents /developers/contracts.json addresses + ABIs of all contracts, chain info, API description · CORS open /chain.json network info, ethereum-lists format /swap/sdk/btcw-swap.js SDK — can be import ed directly from any domain (CORS open) · see JavaScript SDK /logo.svg , /logo.png , /usdw.png BTCw, USDw logos The explorer has its own API (Blockscout): https://explorer.btcw.tech/api/v2/… — see the Blockscout docs."},{"url":"/developers/json-rpc/","title":"JSON-RPC","group":"Reference","heads":[{"id":"endpoints","t":"Endpoints"},{"id":"enabled-namespaces","t":"Enabled namespaces"},{"id":"batching","t":"Batching"},{"id":"subscriptions","t":"Subscriptions"},{"id":"reading-logs","t":"Reading logs"},{"id":"fees-and-transactions","t":"Fees and transactions"},{"id":"limits","t":"Limits"}],"text":"What the public RPC supports, what it does not, and its technical limits. Endpoints HTTPS https://rpc.btcw.tech — POST to the root path / only; GET returns 405. CORS open. WebSocket wss://rpc.btcw.tech/ws — idle connections are kept open for up to 1 hour Client Hyperledger Besu 26.2.0 ( web3_clientVersion ) Enabled namespaces Only eth_* , net_* , web3_* . Other namespaces return -32604 Method not enabled ( debug_* , trace_* , admin_* …) or -32601 Method not found ( txpool_* ). To trace transactions, use the explorer or run a chain fork with anvil (which has debug_traceTransaction ) — see Testing on a chain fork . Batching Send an array of requests in a single HTTP POST. A batch counts as one request against the rate limit — the cheapest way to read many things. A batch of 101 requests has been tested and returned all 101 results. Request body limit: 256 KB ; requests running longer than 30 seconds are cut off. curl -s https://rpc.btcw.tech -H 'content-type: application/json' -d '[ {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_blockNumber\",\"params\":[]}, {\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"eth_call\",\"params\":[{\"to\":\"0xd953d53414a81eb6AC5a4edE73d13992181aF51E\",\"data\":\"0x5c975abb\"},\"latest\"]} ]' Results may come back out of order — match them by id . Subscriptions // wss://rpc.btcw.tech/ws {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"]} {\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"eth_subscribe\",\"params\":[\"logs\",{\"address\":\"0x557aDc3d900c471F3907cdDA3a9F5377d6F9E7e0\"}]} A new block every 5 seconds. Connections may close when the server restarts — reconnect automatically and backfill with eth_getLogs from the last block you processed. Reading logs eth_getLogs has no block-range cap, but the chain is log-heavy (the price-keeping bot runs continuously — about 18,000 logs per 1,000 blocks). Use small ranges (1,000–5,000 blocks) and filter by address + topics so responses stay under 30 seconds. Fees and transactions eth_gasPrice = 0xf4240 (1,000,000 wei) — the minimum the node accepts. eth_maxPriorityFeePerGas = 0; baseFeePerGas = 0. EIP-1559 transactions must set both maxFeePerGas and maxPriorityFeePerGas ≥ 1,000,000 — the base fee is zero, so the effective price is the priority fee. eth_feeHistory works normally. QBFT has instant finality: a receipt in a block is final, no extra confirmations needed. Limits 10 requests/second per IP , with bursts up to 40 (HTTPS) or 20 (WebSocket). Beyond that, HTTPS returns 429 (with CORS headers so browsers can read the error) and WebSocket refuses new connections with 503. Details: Limits & error codes . Need more? Batch requests, cache results on your side, or subscribe over WebSocket instead of polling."},{"url":"/developers/errors/","title":"Limits & errors","group":"Reference","heads":[{"id":"rate-limits","t":"Rate limits"},{"id":"api-http-status-codes","t":"API HTTP status codes"},{"id":"json-rpc-errors","t":"JSON-RPC errors"},{"id":"contract-revert-codes","t":"Contract revert codes"}],"text":"Rate limits, HTTP codes, JSON-RPC errors and contract revert codes. Rate limits Counted per IP using a leaky-bucket algorithm: an average rate plus an allowed instant burst. Endpoint Rate Burst When exceeded rpc.btcw.tech (HTTPS) 10/second 40 HTTP 429 rpc.btcw.tech/ws (opening a connection) shares the 10/second above 20 HTTP 503 btcw.tech/api/v2/* 20/second 40 HTTP 429 btcw.tech/api/* shares the 20/second above 20 HTTP 503 explorer.btcw.tech/api/* shares the 20/second above 100 HTTP 429 Pages and static files unlimited — — HTTPS RPC and WebSocket share one quota; the three APIs on btcw.tech and the explorer share another. A JSON-RPC batch counts as one request. On 429/503: wait and retry with exponential backoff (e.g. 0.5 s → 1 s → 2 s). Batch many eth_call s together instead of sending them one by one. API HTTP status codes Code When 200 success 400 invalid parameters ( /api/v2/swap/quote ): missing parameter, amount not an integer, unknown asset… — body contains { \"error\": \"…\" } 404 path not found — /api/* and /api/v2/* list the available paths in the routes field 405 RPC received a GET — use POST only 422 quote: no route can fill the order (exceeds liquidity, pool paused, stale price…) — see routes[].error 429 / 503 rate limit exceeded (table above). /api/v2/* also returns 503 while the indexer is starting 502 the API could not read from RPC — retry later JSON-RPC errors Code Meaning -32604 Method not enabled — namespace not exposed ( debug_ , trace_ , admin_ …) -32601 Method not found 3 Execution reverted — eth_call / gas estimation reverted; the error code is in error.data (e.g. \"0x1f2a2005\" ), decode it with the table below -32004 Upfront cost exceeds account balance — the wallet doesn't have enough BTCw for value + gas -32602 Invalid call params — malformed parameters Contract revert codes The first 4 bytes of error.data . The SDK's decodeError() translates these codes into readable reasons. Selector Error Where · what to do 0x71c4efed SlippageExceeded(uint256,uint256) ClpPools, OracleAMM — price moved past minOut ; request a new quote 0xab35696f ContractPaused() ClpPools, OracleAMM — the venue is paused 0xf82ae9a5 PoolDisabled(address) ClpPools — this pool is disabled 0xb5e2025e UnknownPool(address) ClpPools — token has no pool 0x305da342 ExceedsMaxTrade(uint256,uint256) OracleAMM — over 10 BTCw per trade; split the order or use ClpPools 0xa17e11d5 InsufficientLiquidity(uint256,uint256) OracleAMM — not enough liquidity for this order size 0x6fb3b185 StalePrice(uint256) OracleAMM — oracle price older than 300 seconds; wait for the next price update 0x1f8f95a0 InvalidOraclePrice() OracleAMM — oracle returned an invalid price 0x1f2a2005 ZeroAmount() amount too small, result rounds to 0 0x201b580a SameToken() ClpPools — input and output are the same token 0xd92e233d ZeroAddress() recipient is the zero address 0xf4b3b1bc NativeTransferFailed() recipient cannot receive BTCw (contract without a receive function) 0xe450d38c ERC20InsufficientBalance token — insufficient balance 0xfb8f41b2 ERC20InsufficientAllowance token — not enough approve d for the swap contract 0xf80dbaea Expired(uint256) SwapRouter — past deadline ; request a new quote 0x20db8267 InvalidPath() SwapRouter — path is empty or longer than 4 hops 0x8e612d4b UnknownVenue(uint8) SwapRouter — venue does not exist (0 ClpPools, 1 OracleAMM, 2 + token vault index) 0x2cd1bb3d UnsupportedHop(uint8,address,address) SwapRouter — that venue does not have this pair 0x626ade30 ValueMismatch(uint256,uint256) SwapRouter — msg.value differs from amountIn when paying in BTCw 0x9c8d2cd2 InvalidRecipient() SwapRouter — invalid recipient 0xf2fb3812 UnexpectedSender() SwapRouter — BTCw sent directly to the router (not via swap ) 0x9e87fac8 Paused() bridge router — paused 0xfa5cd00f NotAllowed(address) bridge router — allowlist enabled and the wallet is not on it 0x7c98bbbc BadGranularity(uint256,uint256) 482120 router — amount not divisible by 10 12 0xf97da669 PriceJumpTooLarge(int256,int256) BtcwPriceFeed — price update changed by more than 10% (only if you are the updater) 0x9a280f39 NotUpdater() BtcwPriceFeed, PriceHub — caller is not an authorized price updater"},{"url":"/developers/contracts/","title":"Contract reference","group":"Reference","heads":[{"id":"usdw","t":"USDw"},{"id":"usdw-functions","t":"USDw — functions"},{"id":"usdw-events","t":"USDw — events"},{"id":"oracleamm","t":"OracleAMM"},{"id":"oracleamm-functions","t":"OracleAMM — functions"},{"id":"oracleamm-events","t":"OracleAMM — events"},{"id":"oracletokenamm-ethw","t":"OracleTokenAMM_ETHw"},{"id":"oracletokenamm-ethw-functions","t":"OracleTokenAMM_ETHw — functions"},{"id":"oracletokenamm-ethw-events","t":"OracleTokenAMM_ETHw — events"},{"id":"oracletokenamm-ethw-errors","t":"OracleTokenAMM_ETHw — errors"},{"id":"swaprouter","t":"SwapRouter"},{"id":"swaprouter-functions","t":"SwapRouter — functions"},{"id":"swaprouter-events","t":"SwapRouter — events"},{"id":"swaprouter-errors","t":"SwapRouter — errors"},{"id":"clppools","t":"ClpPools"},{"id":"clppools-functions","t":"ClpPools — functions"},{"id":"clppools-events","t":"ClpPools — events"},{"id":"btcwpricefeed","t":"BtcwPriceFeed"},{"id":"btcwpricefeed-functions","t":"BtcwPriceFeed — functions"},{"id":"pricehub","t":"PriceHub"},{"id":"pricehub-functions","t":"PriceHub — functions"},{"id":"bridge-router-guardedcollateralrouter","t":"Bridge router (GuardedCollateralRouter)"},{"id":"bridge-router-guardedcollateralrouter-functions","t":"Bridge router (GuardedCollateralRouter) — functions"},{"id":"bridge-router-guardedcollateralrouter-events","t":"Bridge router (GuardedCollateralRouter) — events"},{"id":"bridge-router-guardedcollateralrouter-errors","t":"Bridge router (GuardedCollateralRouter) — errors"}],"text":"Functions, events and errors of the public contracts — generated from contracts.json. This page is generated automatically from /developers/contracts.json — it covers only the public interface integrators need. Full source code (including the Owner's admin functions) is verified on the explorer ; admin permissions are summarized in Trust model . Revert codes: Limits & error codes . Compiler: Solidity 0.8.28, optimizer 200 runs (482120-side contracts). Bridge routers: @hyperlane-xyz/core 12.1.0. USDw Address 0x3b5BD179CA6Aab4DAF0f842829776843ABe6e727 · chain 482120 · 18 decimals USDw — functions 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) USDw — events event Transfer(address indexed from, address indexed to, uint256 value) OracleAMM Address 0xd953d53414a81eb6AC5a4edE73d13992181aF51E · chain 482120 BTCw <-> USDw at the oracle price +/- spreadBps (30 = 0.3%). maxTradeBtcw per order; stops when the oracle is older than maxAgeSec. OracleAMM — functions 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) OracleAMM — events event Swap(address indexed trader, bool btcwIn, uint256 amountIn, uint256 amountOut, uint256 priceWad) OracleTokenAMM_ETHw Address 0xFd6d78A3b60175D37146793396A2FA7864039D54 · chain 482120 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). OracleTokenAMM_ETHw — functions 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) OracleTokenAMM_ETHw — events event Swap(address indexed trader, address indexed to, bool tokenIn, uint256 amountIn, uint256 amountOut, uint256 priceWad) OracleTokenAMM_ETHw — errors 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 · chain 482120 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. SwapRouter — functions 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) SwapRouter — events event Routed(address indexed sender, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut, address to, uint256 hops) SwapRouter — errors 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 · chain 482120 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). ClpPools — functions 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 to"},{"url":"/developers/sdk/","title":"JavaScript SDK","group":"Reference","heads":[{"id":"installation","t":"Installation"},{"id":"quick-start","t":"Quick start"},{"id":"functions","t":"Functions"},{"id":"constants","t":"Constants"},{"id":"notes","t":"Notes"}],"text":"btcw-swap.js: a dependency-free ES module shared with the quote API. /swap/sdk/btcw-swap.js — a single-file ES module with no dependencies (only fetch and BigInt ), running in the browser, Node 18+, Deno and Bun. Quotes are read straight from the contracts over RPC. The /api/v2/swap/quote API runs this same file on the server, so both always return the same numbers. Installation There is no npm package. Two options, same file: Option When to use Import directly from btcw.tech fastest — nothing to download. The file is served with CORS ( Access-Control-Allow-Origin: * ), so a page on any domain can import it in the browser; cached for 5 minutes. Always the latest version. Copy the file and pin the version when you need a fixed build: serve the file yourself and decide when to upgrade (check VERSION ). Node and Bun cannot import from an https:// URL — use this option. // Option 1 — import directly (browser: <script type=\"module\">; Deno) import { getQuote } from \"https://btcw.tech/swap/sdk/btcw-swap.js\"; // Option 2 — copy it locally, then import the local file // curl -sO https://btcw.tech/swap/sdk/btcw-swap.js // grep 'export const VERSION' btcw-swap.js # currently \"1.2.0\" import { getQuote } from \"./btcw-swap.js\"; /developers/contracts.json (addresses + ABIs) is also served with CORS — read it directly with fetch(\"https://btcw.tech/developers/contracts.json\") from your page. Quick start 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] }); Functions Name Description getQuote(p) quote + build the transaction. p : from , to (address or \"native\" ), amount (base units), sender? , recipient? , slippageBps? (default 100), route? ( \"best\" | \"clp\" | \"amm\" | \"amm-eth\" | \"router\" ), rpc? . Returns the same structure as the quote API getAssets({ api? }) the list of 202 assets from /api/v2/swap/assets waitForReceipt(hash, { rpc?, timeoutMs? }) waits for the receipt (default up to 120 seconds); status \"0x1\" means success decodeError(e) maps a revert code (hex string or RPC error object) to a readable reason, or null normalizeAsset(a) normalizes an asset id: returns NATIVE or a lowercase address; throws if invalid rpcBatch(calls, rpc?) sends a JSON-RPC batch (counts as one request against the rate limit) Constants Name Value VERSION SDK version CHAIN chainId , chainIdHex , name , rpc , explorer , nativeCurrency CONTRACTS clp , amm , usdw , ethw , ethAmm (ETHw oracle-priced vault), router (SwapRouter) NATIVE 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE — native BTCw DEFAULT_API https://btcw.tech/api/v2 ERRORS the selector → reason table used by decodeError ADD_CHAIN_PARAMS EIP-3085 parameters for wallet_addEthereumChain WAD 10n ** 18n Notes All amounts are BigInt or integer strings in base units (18 decimals for every asset). There is no on-chain deadline — minAmountOut is the protection; if a quote is past expiresAt (30 seconds), request a new one. Approvals are always for the exact amount of the order, never unlimited. With a chain fork: pass rpc: \"http://127.0.0.1:8545\" — see Testing on a chain fork ."},{"url":"/developers/status/","title":"Status","group":"Operations","heads":[{"id":"right-now","t":"Right now"},{"id":"check-it-yourself","t":"Check it yourself"},{"id":"during-an-incident","t":"During an incident"}],"text":"Chain, oracle, swap and bridge health — read live when you open the page. Every tile below is read live when you open the page (public RPC and API) and refreshes every 20 seconds. If a read fails, it shows \"unavailable\" — no guessing. Right now Latest block … BTCw price (oracle) … OracleAMM … ClpPools … 200 token prices (PriceHub) … Bridge — 482120 side … Bridge — Arbitrum side … API /api/v2 … Check it yourself curl -s https://btcw.tech/api/health # price API: ok, block, age_seconds curl -s https://btcw.tech/api/v2/health # indexer: ok, lastBlock, builtAgoS curl -s https://btcw.tech/api/v2/stats | jq .status cast call 0x984fDC24D1A2Be538C7290487d0fc212635D98A5 \"paused()(bool)\" --rpc-url https://arb1.arbitrum.io/rpc During an incident Stale price ( stale: true ): OracleAMM and OracleRebalancer stop automatically; ClpPools keeps filling at the pool price. Chain halted (block number not increasing): everything on 482120 stops until the chain resumes. Bridge delayed : sent messages are not lost; the relayer retries automatically. When paused, paused() = true . There is no dedicated incident page yet. Detailed metrics: Dashboard ; genesis and block cadence: Transparency ."},{"url":"/developers/changelog/","title":"Changelog","group":"Operations","heads":[{"id":"30-sep-2026","t":"30 Sep 2026"},{"id":"29-sep-2026","t":"29 Sep 2026"},{"id":"28-sep-2026","t":"28 Sep 2026"},{"id":"25-sep-2026","t":"25 Sep 2026"},{"id":"23-sep-2026","t":"23 Sep 2026"}],"text":"Changes that affect integrators, newest first. Only changes that affect integrators are listed. R-xx ids are the project's internal decision numbers. 30 Sep 2026 SwapRouter (R-49) 0x397E…31E4 : chains multiple hops (ClpPools, OracleAMM, ETHw vault) into one transaction , e.g. BTCw → ETHw. Quote API + SDK 1.2.0 add the router route; contracts.json adds SwapRouter . ETHw as a bridge destination (R-48): ETHw ⇄ USDw oracle-priced vault 0xFd6d…9D54 ( OracleTokenAMM , ± 0.3%, max 300 ETHw per trade); the Bridge page lets you receive/send ETHw the same way as BTCw. contracts.json adds OracleTokenAMM_ETHw . Quote API and SDK 1.1.0 add the amm-eth route (also used by the Swap tab). SDK importable directly from btcw.tech : /swap/sdk/btcw-swap.js and /developers/contracts.json are served with CORS ( Access-Control-Allow-Origin: * , 5-minute cache) — pages on other domains can import / fetch them in the browser. 100 US-stock price-tracker tokens (R-47): NVDAw, AAPLw, MSFTw… — 100 new pools, 201 pools / 202 assets in total. PriceHub prices them from 5 stock-market quote sources; OracleRebalancer is the minter and the keeper that holds pool prices, as for the crypto tokens. /api/v2/pools adds the class field; the Dashboard and Swap page have Crypto · US Stock · Stablecoin tabs. Multi-page docs replace the single-file page. Old /developers/#… links redirect to the new pages; search is available ( / key). Swap quote API for third-party wallets (R-46): /api/v2/swap/assets , /api/v2/swap/quote and SDK /swap/sdk/btcw-swap.js 1.0.0 — any pair among 102 assets can be swapped, with a prebuilt transaction returned. 29 Sep 2026 OracleRebalancer (R-45) 0x6c811003…2e69 becomes the minter of the 100 price-tracker tokens and pulls pool prices back to the oracle (mint cap 300 BTCw/day). Its volume is counted in of_which_treasury ; /api/v2/stats adds treasury_addresses . Bridge open to all wallets (R-44): allowlistEnabled = false on all 4 routers. The Bridge page adds an option to receive/send BTCw directly. /developers/ and /developers/contracts.json published. 28 Sep 2026 Hyperlane bridge Arbitrum ⇄ 482120 (R-39, R-40): two routes, USDC and USD₮0 ⇄ USDw, initially allowlisted wallets only. 100 price-tracker tokens + 101 CLP pools (R-35), Dashboard and the /api/v2/stats|pools|history|swaps API (R-36), Swap page (R-41). BETA label (R-38) on every page: live mainnet, features still being finished. On-chain names, symbols, errors and events follow the English EVM convention (R-34). 25 Sep 2026 USDw 0x3b5B…e727 and OracleAMM 0xd953…F51E go live (R-32) — all contract parameters are adjustable after deployment (R-31). Public price API /api/price , /api/tokens , /api/health (R-30). 23 Sep 2026 Main site live at btcw.tech (R-26); the Transparency page publishes the genesis (R-27)."},{"url":"/developers/security/","title":"Security","group":"Operations","heads":[{"id":"scope","t":"Scope"},{"id":"reporting-a-vulnerability","t":"Reporting a vulnerability"},{"id":"do-not-test-on-mainnet","t":"Do not test on mainnet"},{"id":"risk-mitigations-in-place","t":"Risk mitigations in place"}],"text":"Scope, how to report a vulnerability, and what not to do on mainnet. Scope Vulnerabilities in the contracts listed under Contract addresses , the APIs and pages on btcw.tech , the rpc.btcw.tech RPC, and the Hyperlane bridge. The published trust model (the Owner permissions listed in Trust model ) does not count as a vulnerability. Reporting a vulnerability A public reporting channel has not been set up yet . In the meantime, do not publish vulnerability details before the issue is fixed. Bug bounty program: none . When reporting, include: the affected contract or endpoint, reproduction steps — ideally on a chain fork with anvil — and the estimated impact. Do not test on mainnet Do not attempt exploits on the live chain — use a chain fork ; everything on mainnet is real money and irreversible. Do not flood the RPC or API to probe the limits — they are documented in Limits & error codes . Never use private keys printed by anvil or shown in these docs for real wallets. Risk mitigations in place Source code is public and verified on the explorer (482120) and Sourcify (Arbitrum). Foundry tests and rehearsals on a fork of the live chain before major deployments — e.g. OracleRebalancer: 20/20 tests passed, 8/8 mutants caught. Oracle price jumps capped at ≤ 10% per update; daily token mint cap; granular pausing. No independent audit yet — see Trust model & risks ."}]}