← Back

The worst of both worlds

Bitcoin is boring by today’s standards. That’s a big part of why I like it.

I work mostly in the Ethereum ecosystem, where bullshit is everywhere and there’s always something new to keep up with.

So, naturally, I wanted to bring some of that to Bitcoin. Just for a while.

Could I have something as ubiquitous as stablecoins on Bitcoin?

The version I wanted was simple to describe: the tokens stay on the chain where they already live, and Bitcoin moves the right to withdraw them. The final owner should be able to collect without asking me, an operator, or any previous owner for permission.

The dollars stay put

Start with 100 stablecoin tokens, each intended to track one dollar. Lock them in a smart contract, which is just code on their blockchain that controls when they can leave. I’ll call it the vault.

The vault ties those tokens to a particular Bitcoin output. An output is a piece of bitcoin that a key can spend. Spending it consumes it and creates new outputs. Bitcoin calls an unspent one a UTXO.

The chosen output carries the withdrawal right. Alice owns it, then Bob, then Carol. The 100 tokens sit in the vault the whole time.

The right to withdraw changes hands; the tokens stay in the vault Alice passes the note to Bob, who passes it to Carol. The 100 tokens stay in the same vault throughout. WHO HOLDS THE NOTE Aliceholds the note Bobholds the note Carolholds the note sale sale Vault: 100 tokens stays here
Each new owner gets the right to withdraw the same 100 tokens.

The withdrawal right is the note; the Bitcoin output carrying it is the carrier. Bitcoin only sees ordinary bitcoin changing hands. The vault gives that history its extra meaning.

For now, each note is indivisible. You can pass the whole 100-token claim along or redeem it. Making change can wait.

One transaction for the trade

Bob wants to buy Alice’s note with bitcoin. They prepare one transaction:

Alice’s carrier ──→ Bob’s new carrier
Bob’s bitcoin   ──→ Alice’s payment + Bob’s change
                    (minus the miner fee)

Both sign their own inputs. Once Bitcoin records this transaction in an accepted block, it is confirmed: the payment and the carrier move together. Bob doesn’t have to pay first and hope Alice sends the note afterwards.

There would be no call to the stablecoin vault for each sale. Only the initial deposit and eventual redemption need to happen there. That’s the part I want to keep.

What am I actually signing?

The experiment uses Taproot, a Bitcoin spending format, and Schnorr signatures. Alice signs a hash of the transaction data: a fixed-size fingerprint of exactly what she is authorizing. In the signing mode I used, this commits to all the inputs, their amounts and spending rules, and the outputs. Change her payment and the signature stops checking out.

The check comes down to an equation:

s·G = R + e·Q

Q is Alice’s public key. R and s come from her signature. G is a fixed point on an elliptic curve, and e is a hash binding the key, signature point and message together. Anyone can check that the signature matches Alice’s key and the transaction, without learning her secret key.

I can perform that check inside the vault too. This made a shortcut look tempting: follow the signed transfers from Alice to Bob to Carol, then pay whoever signs the final withdrawal.

Then try to steal it

Alice sells the note to Bob. The sale confirms. She still knows her old private key, so she can sign another transaction spending her old carrier, this time withdrawing the tokens to herself.

Bitcoin rejects the second spend. But a vault that only checks signatures can accept her claim if it hasn’t heard about the sale.

Bob has a trick too. He gets Alice’s signature on their sale, then spends his payment coins somewhere else. That payment confirms, making their sale impossible in the same Bitcoin history. Yet he still has a signed sale, and he can add his own signed withdrawal. The signature-only vault can pay him even though Alice never got paid.

I reproduced the attack locally and got the vault to pay Bob using a sale that could never confirm.

My prototype gave someone time to submit evidence of the conflicting spend. That works only if somebody notices and acts before the deadline. I don’t want owning the note to come with an obligation to watch it all the time.

The vault needs proof that these transactions confirmed on Bitcoin before it pays.

Make the withdrawal happen on Bitcoin

First, it must have received the exact 100 tokens and registered the initial carrier. Every sale links that carrier to the next one. To redeem, Carol spends the final carrier into an unspendable output containing a hash of the note and her payout address. Once that transaction confirms, the note’s path ends. It’s called a burn; the stablecoins are still waiting in the vault.

The hash also binds the withdrawal to this chain, vault and token. Someone copying Carol’s proof can only cause a payment to Carol.

A confirmed Bitcoin withdrawal unlocks the stablecoins Carol burns the carrier on Bitcoin. The vault checks the connected ownership history and confirmed burn against its Bitcoin chain verifier, then pays Carol once. BITCOIN STABLECOIN CHAIN Carol burns the carrier Vault checks history + burn proof 100 tokens to Carol Bitcoin chain verifier
The vault checks the proof at payout and marks the note redeemed. It can pay only once.

Carol submits the connected transaction history and proof that the transfers and burn are in the vault’s accepted Bitcoin chain. It waits for more blocks to build on the burn’s block, reducing the risk of a reversal. The contract checks the evidence, marks the note redeemed and transfers the tokens. If the token transfer fails, that entire operation reverts.

With a correct view of Bitcoin, Alice’s second spend cannot confirm alongside her sale. Bob’s sale cannot confirm alongside his conflicting payment. Neither attack produces the history the vault requires.

How does the vault know?

A Merkle proof links a transaction to a block through a short sequence of hashes. The block’s header contains the final hash. This proves the transaction is in that block.

The vault also needs to know which blocks to accept. A light client checks Bitcoin headers: how they link, their mining work, difficulty changes, timestamps and competing branches. Anyone can submit updates, including the person redeeming. No updater gets a key that approves withdrawals.

There’s still a catch. Checking headers means relying on miners to enforce the transaction rules, and somebody has to keep the vault’s view of Bitcoin up to date. If an attacker can hide the public chain and feed it a private fork, the chain with the most mining work that the vault has seen might be the wrong one.

This depends on someone keeping the vault’s view of Bitcoin current while individual holders are offline.

Recursive validity proofs could check more of Bitcoin’s rules and compress that work into a small proof. They still need a chain-selection rule and incoming data. They also can’t undo a stablecoin payment after a deep Bitcoin reorganization, or stop a stablecoin issuer freezing tokens.

What I’ve tried so far

I found other people working on closely related problems. Atomiq’s UTXO-controlled vaults already use verified Bitcoin transactions to order withdrawals on another chain. They’re built for swaps, but the mechanism is close to what this note needs.

I ran Tacit’s relay contract locally against eight real Bitcoin headers crossing a difficulty adjustment. All ten checks passed, including rejection of bad mining work, invented difficulty and insufficient confirmation depth. It started from a trusted checkpoint.

I also built Hazync’s verifier and checked an externally generated recursive proof covering blocks 1–114,091. Changing its public result broke verification. The proof ends in 2011. I still need to test a withdrawal using recent blocks.

I haven’t connected these pieces into the finished vault yet. So far, I’ve used local chains and historical public data. No real stablecoins.

Next I want to connect the verifier to the vault, confirm a sale, take Bob offline, and let Alice try to withdraw. Then try the buyer’s attack with Alice offline. Both should fail without the honest person having to do anything. That’s the version I’d be willing to call a working prototype.