Withdraw BTC
To move BTC back to Bitcoin, you submit a withdrawal request on Sui that burns
your hBTC and names the Bitcoin address that receives the BTC. You can make
the request with the Hashi CLI or with the TypeScript SDK. This page covers the
steps. For what the committee and the Guardian do with your request, see the
Withdraw lifecycle.
- Prerequisites
- A Sui address holding
hBTCand funded with SUI for gas, and its private key in one of the accepted key formats. - A Bitcoin address you control on the network that matches your Sui network. See the Networks table.
- For the CLI: a
hashibinary built as described in Software dependencies and a CLI config file from CLI config. - For the SDK: a Node.js project with
@mysten/hashiand@mysten/suiinstalled as described in TypeScript SDK.
Before you withdraw
Keep the following rules in mind:
- Minimum amount: Each request must be at least
bitcoin_withdrawal_minimum. The minimum guarantees that the request can pay the worst-case miner fee and still leave an output above Bitcoin's dust threshold. - Accepted destination addresses: Hashi pays
P2WPKH(bech32,bc1qon Mainnet) andP2TR(bech32m,bc1pon Mainnet) addresses. It does not supportP2WSHor legacy address types. The address must belong to the Bitcoin network that matches your Sui network. - You pay the miner fee: Hashi charges no protocol fee, but it deducts the
Bitcoin miner fee from your output. The amount of BTC you receive is less
than the
hBTCyou burn. See Fees for how Hashi bounds the fee. - Withdrawals queue: Your request waits for committee approval, batching with other requests, and capacity in the Guardian's rate limiter. See How long does a withdrawal take?.
Withdraw with the CLI
CLI global options such as -c belong after the first-level subcommand, so
-c goes after withdraw. See
Global options.
Step 1: Submit the request
Run the following command with the amount in satoshis and your destination address:
hashi withdraw -c hashi-cli.toml request --amount <sats> --btc-address <addr>
The command burns the hBTC from your address, places the request in the
withdrawal queue, and prints the withdrawal request ID. Keep the ID. You need
it to check status and to cancel.
Step 2: Track the request
Check one request, or list all of them:
hashi withdraw -c hashi-cli.toml status <request-id>
hashi withdraw -c hashi-cli.toml list --json
The request moves through the states Requested, Approved, Processing, Signed, and Confirmed. After Hashi commits the Bitcoin transaction, the status includes its txid, which you can follow in any Bitcoin explorer.
Withdraw with the TypeScript SDK
The SDK attaches to a Sui client and exposes every Hashi method under
client.hashi.*. Install it and create a client as described in
TypeScript SDK.
Step 1: Check fees and limiter capacity
Before you submit, you can read the worst-case miner fee, the onchain minimum, and whether the Guardian's rate limiter can sign your amount right now:
// Withdrawal: worst-case BTC network fee + on-chain minimum. Pass a sender
// to also get a dry-run gas estimate.
const fees = await client.hashi.view.withdrawalFees(signer.toSuiAddress());
// { worstCaseNetworkFeeSats, withdrawalMinimumSats, gasEstimateMist }
// Can the guardian sign a 50,000-sat withdrawal right now?
const check = await client.hashi.guardian.canWithdraw(50_000n);
// { allowed, availableNowSats, estimatedWaitSecs }
Step 2: Submit the request
The SDK decodes the destination address as bech32 or bech32m and checks that it matches the client's Bitcoin network before signing:
const result = await client.hashi.requestWithdrawal({
signer,
amountSats: 50_000n,
bitcoinAddress: 'tb1q…', // P2WPKH on signet/testnet, or `tb1p…` for P2TR
});
if (result.$kind !== 'Transaction') {
throw new Error(`request failed: ${JSON.stringify(result.FailedTransaction)}`);
}
// Pull the request id out of the WithdrawalRequested — needed if
// you later want to cancel.
const evt = result.Transaction.events?.find((e) =>
e.eventType.endsWith('::withdrawal_queue::WithdrawalRequested'),
);
const requestId = (evt as { json?: { request_id?: string } } | undefined)?.json?.request_id;
Keep requestId if you might cancel the withdrawal.
Step 3: Track the request
Use the transaction digest to check status once or to wait for a terminal state:
const withdrawal = await client.hashi.view.withdrawalStatus(digest);
// withdrawal?.status: "Requested" | "Approved" | "Processing"
// | "Signed" | "Confirmed" | "cancelled"
const wInfo = await client.hashi.waitForWithdrawal(digest);
// wInfo.btcTxid is populated once the committee commits the Bitcoin tx.
If the transaction is slow to confirm
Hashi does not replace a slow Bitcoin transaction with a higher-fee version. If your withdrawal sits in the mempool, you can speed it up yourself by spending your output with a higher-fee child transaction, which pays for the parent. See Stuck transactions.