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

Bridge funds

Deposit USDC / USD₮0 from Arbitrum as USDw on 482120, and withdraw back.

Guides › Bridge funds

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

# 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

  1. approve(router, amount) on the source-chain token — the exact amount.
  2. transferRemote(uint32 destination, bytes32 recipient, uint256 amount) on the source-chain router, msg.value = 0.
  3. destination: 482120 to deposit, 42161 to withdraw.
  4. recipient: destination wallet address left-padded with zeros to 32 bytes (Hyperlane standard).
  5. 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.