← unravel rule

How it works

Somewhere a committee decided that withdrawing your own money needs a signed confession saying which coins are yours. Exchanges comply by collecting signatures, xpubs and test payments, then storing them in the same databases that keep ending up on hacker forums. This service exists so you can satisfy the form without feeding the panopticon. The rest of this page explains how it works and what you're trusting.

In short: you get a hop address that can sign the exchange's message. You withdraw to it, and it forwards everything to the destination address you chose, usually your cold wallet, whose key never signs anything for the exchange.

The hop address

It is a taproot address with two ways to spend:

The server sends its key, and your browser builds the address itself from the destination address, its own key and the server's key. If the server's address differs, the page refuses to use it. A server can't hand you an address without the safety net.

Signing the exchange's message

The page and the server sign together through the normal path. The result is an ordinary BIP-322 signature in the "simple" format: a base64 witness, the same kind a single-key taproot wallet produces. The page only signs the exact message you pasted in.

The forward

While the page is open, it asks the server about the address every few seconds. Once a deposit has one confirmation, the server proposes a transaction, and your browser checks it before signing:

These checks only stop a forward that pays someone other than the destination, or burns the deposit on fees. The server always proposes one that passes, and it can't move the funds without the page's signature. The server looks up the address, not a transaction id, because exchanges often fee-bump withdrawals, which changes the id. You can reuse the address for later withdrawals.

The safety net

If you lose this browser, or the server goes away, the destination's wallet can claim the funds through the hidden script. Whoever holds the destination's key holds this path: usually that's you, but if the destination is someone else's address, the safety net is theirs. It needs two things:

Taproot addresses that are script-only multisig have no usable key inside them, so the safety net can't work for them. Older bc1q addresses aren't accepted at all.

Emergency kit

Each browser gets one rescue key, 16 random bytes plus a 2-byte checksum, written as nine groups of four hex digits. The kit is a text file holding it and the server's public key. The page builds the browser key for each destination address from the rescue key, so one kit covers every hop address the browser makes with that server. The rescue key never changes, but switching server means saving a new kit. The page shows you a hop address once you've downloaded or copied the kit, and you can check the saved file at step 2 afterwards. Restoring the kit at step 2, in a browser that hasn't saved a kit of its own, brings back the hop addresses it covers, so you can forward from another device or after your browser clears its storage.

Keep it private. With it and the destination address, someone can find your hop addresses and rebuild your browser's half of the shared key. That half can't move funds without the server's.

Anyone can rebuild a hop address from the kit without the server:

R = the rescue key's first 16 bytes
b = TaggedHash("unravel/browser-key/v1", R || scriptPubKey(destination address)) mod n
B = b·G
hop address = tr(MuSig2(B, S), pk(D))

TaggedHash is the one from BIP-340. MuSig2 is BIP-327 key aggregation of B then S, in that order, not sorted. S is the server's key and D is the key inside the destination address. The recovery page does this for you, and is one self-contained file you can save and open offline, with the server gone.

Who you trust, and for what

No address design removes that last point on Bitcoin today. Without covenants (opcodes that restrict where coins can go), some key other than the destination's has to be able to spend, and whoever holds it could take the funds.

Checking the address yourself

Step 3's "verify" shows the address's spending rules as a descriptor:

tr(<shared key>,pk(<the destination's key>))#checksum

Give it to Bitcoin Core with bitcoin-cli deriveaddresses "<descriptor>", and it should print your hop address. The key in pk(…) should match the destination address: it is the 32 bytes encoded inside it.

Privacy

The exchange sees one ordinary taproot address that later pays the destination, which is no more than it would learn if you had withdrawn straight there. It never sees your xpub. The hidden script stays hidden unless the safety net is used. The server does learn which hop address belongs to which destination address, the exchange's message you sign, which can name your account, and your deposits. Like any website, it also sees your IP address. Use a fresh receive address as the destination, and a leak shows only that one payment.

Fees

There's no service fee. The destination receives the deposit minus the forward's network fee. The server pays a little over the mempool's going rate for the next block, but never less than 1 sat/vB or more than 10, which keeps a forward at or under 1,110 sats. If it can't get a fee estimate, it waits rather than guess. When fees are higher, the forward still goes out and takes longer to confirm, and step 5 warns you before you withdraw. Step 5 also shows the current fee and the smallest deposit that can be forwarded. Some exchanges also take their withdrawal fee out of the amount, so if you're paying someone an exact sum, send a little extra.

Status

This is extremely experimental and runs on Bitcoin mainnet with real bitcoin. Use it at your own risk.

report issue