Submarine Swaps

Published in
3 min readFeb 19, 2019


It is not possible to simply send Bitcoin from a lightning channel to a regular on-chain address. Submarine Swaps, which are based on the same principals as regular atomic swaps, provide a solution for this problem. A Submarine Swap is a way to trustlessly exchange coins from lightning to on-chain addresses and vice versa using a service provider like Boltz.

Flow of a Submarine Swap (on-chain -> lightning)

The flow of a Submarine Swap, in which the user sends funds on the chain and receives on lightning is described as follows:

Firstly, the user reviews the exchange rate provided by Boltz, clicks “Start Swap” and pastes an invoice. The Boltz backend gets the hash of the preimage from that invoice and generates a P2(W)SH address to which the user is asked to send funds to. The redeem script of such a P2SH address looks like this:

HASH160 <hash of the preimage> EQUAL
IF <public key of Boltz>
ELSE <timeout block height> CHECKLOCKTIMEVERIFY
DROP <public key of the user> ENDIF

At first glance this script may seem a little eerie but when you have a close look it will be easy to understand:

When trying to spend the UTXO of the redeem script above, HASH160 causes the first argument (which is the preimage) to be hashed with SHA256 and afterwards with RIPEMD-160. SHA256 because the hash of the preimage that is retrieved from the invoice is already hashed with that function and RIPEMD-160 is used to make the transactions a little bit smaller. The next element of the script, <hash of the preimage>, is the RIPEMD-160 hash of the SHA256 hash of the preimage from the invoice. EQUAL means that if the preimage is correct the IF will be triggered and the public key of Boltz will be loaded. If EQUAL is false the ELSE clause will get triggered.
<timeout block height> in combination with CHECKLOCKTIMEVERIFY checks if the time lock is already expired. If not, the redeeming transaction will be rejected until the time lock runs out (fun fact: BTCD allows transactions with time locks that are not expired yet in the mempool while Bitcoin Core simply rejects them). DROP removes the <timeout block height> and the public key of the user is loaded afterwards.

ENDIF ends the if clause and CHECKSIG checks whether the signature provided is valid for the loaded public key. If true, the script ends successfully and the transaction is valid and can get included in a block.

The next step is that the user sends funds to the aforementioned P2SH address. If the user sends the before agreed amount, Boltz will pay the invoice the user pasted in the first step and get the preimage. With the preimage the on-chain coins can be claimed by Boltz and both parties get the assets they want. The swap completed.

If something goes wrong before the user sends any coins, the swap simply stalls and nothing will happen. If the user already sent the on-chain coins, a refund has to be done. By simply setting the preimage (first argument) of the spending transaction to an empty string the ELSE will be triggered and if the time lock is already expired the user has their funds back.

Flow of a reverse Submarine Swap (lightning -> on-chain)

Swaps from lightning to on-chain coins are called reverse Submarine Swaps and are based on the same principles and scripts but the user and Boltz backend switch roles.

A reverse Submarine Swap starts with Boltz generating an invoice and creating the corresponding P2SH address and also sending funds to it. When the user pays the invoice it will get the preimage in return which will allow the user to claim the on-chain coins — resulting in successful swap.

If something fails or the user simply doesn’t pay the invoice until the time lock expires, Boltz will automatically refund the on-chain coins and send them back to its wallet.