intermediate
+200 XP

Building on Hashi

Wire @mysten/hashi into a real app โ€” construct the client, derive a deposit address, watch the Bitcoin chain yourself, submit and track a deposit, then burn hBTC back into native BTC.

Lesson Syllabus

Setting Up the Client
๐Ÿ”Œ

Install and Extend

Hashi ships as `@mysten/hashi` with `@mysten/sui` as a peer dependency. You never construct a Hashi client directly โ€” you build a normal Sui client and `$extend` it with `hashi()`. Every method then lives under `client.hashi.*`. The Bitcoin network is derived from the Sui client, not passed in: testnet and devnet both map to Bitcoin **signet**. Mainnet is not deployed, so `hashi()` throws there.

๐Ÿงฌ

Deriving the Deposit Address

Each Sui address maps to exactly one Bitcoin deposit address. `generateDepositAddress({ suiAddress })` derives a **P2TR** taproot address (bech32m, `tb1p...` on signet) from the on-chain MPC master key and the guardian key. It is deterministic โ€” regenerate it whenever you need it instead of storing it. There is no registration step and no per-user setup transaction.

๐Ÿ”

The Read Layer

Before you write a transaction, read. The `view.*` namespace covers everything an integration needs: the governance snapshot, hBTC balances, per-digest status, gas and fee estimates, and a merged transaction history. Balances come back as `bigint` satoshis โ€” hBTC has **8 decimals**, matching Bitcoin exactly, so one satoshi of hBTC is one satoshi of BTC.

The Deposit Flow, End to End
๐Ÿ›ฐ๏ธ

You Watch Bitcoin, Not the SDK

This is the single biggest surprise for integrators: `@mysten/hashi` does **not** scan the Bitcoin chain. `deposit()` demands the funding `txid`, `vout`, and `amountSats` as inputs. Finding them is your job โ€” mempool.space, an Esplora instance, or your own node. The bundled `bitcoin.*` helpers only work if you construct the client with a `btcRpcUrl` pointing at a verbose Bitcoin Core JSON-RPC node.

๐Ÿ“ฅ

Submitting the Deposit

With the funding output in hand, `deposit()` records the UTXO on Sui. It runs three preflight checks before it signs anything โ€” structural validation, a pause check, and the per-UTXO minimum โ€” then executes. Note the shape of the call: `recipient` is a separate field from `signer`, which means a relayer can pay the Sui gas while hBTC mints to a user who holds no SUI at all.

โณ

Tracking to Mint

The Sui `deposit` call only registers the UTXO. Minting takes two more transactions and roughly **70 minutes**: six Bitcoin confirmations at about ten minutes each, then a committee BLS approval carrying over two-thirds of stake, then a 10-minute delay window before `confirm_deposit` becomes callable. `depositStatus(digest).confirmableAtMs` gives you the exact earliest mint time.

Redeeming Native Bitcoin
๐Ÿ“ค

Requesting a Withdrawal

`requestWithdrawal` escrows the hBTC on-chain and enqueues a request for the committee. The destination must be a bech32 P2WPKH or bech32m P2TR address whose HRP matches the configured network โ€” it is decoded client-side, so a wrong-network address throws `InvalidBitcoinAddressError` before you spend gas. The minimum out is the same 30,000 sats as the minimum in.

๐Ÿ”€

The Path Back to Bitcoin

After the request, the committee drives four more steps. Approval carries a certificate. The commit step selects UTXOs, burns the escrowed hBTC, and locks the inputs. Then MPC signatures are recorded per input, the guardian co-signs at finalize, and confirmation marks the inputs spent. Every status is readable from `view.withdrawalStatus(digest)`.

โš–๏ธ

Cancel Windows and Fees

Two things decide whether a user can back out, and one thing you can never paper over. Cancellation is owner-only, blocked until a 1-hour cooldown elapses, and impossible once the request reaches Processing. And the Bitcoin miner fee is deducted from the withdrawal output in BTC โ€” no amount of Sui gas sponsorship touches it.