Overview
Bitcoin deposits use a UTXO-based model. Thecall_data from the deposit action is a numeric memo that must be embedded in the transaction as an OP_RETURN output. This memo is how Layerswap identifies and matches your deposit.
Prerequisites:
- bitcoinjs-lib for PSBT construction
- @bitcoinerlab/secp256k1 for elliptic curve operations
- Access to a Bitcoin node or the Mempool.space API for UTXO data and fee estimates
call_data Format
For Bitcoin,call_data is the swap’s numeric memo followed by a ; terminator — e.g. "11177265;".
Convert the numeric part to hex (use BigInt — Number can lose precision and chokes on the ;) and
keep the terminator and anything after it unchanged:
OP_RETURN output in the transaction. Layerswap reads only the part
before the ; to match the deposit, so you can append your own data after the terminator — as long
as the whole OP_RETURN payload stays within the 80-byte limit.
Transaction Construction
1
Fetch UTXOs
Retrieve unspent transaction outputs for the sender’s address. You can use the Mempool.space API or your own Bitcoin node.
2
Fetch raw transactions
For each UTXO, fetch the full raw transaction hex. This is needed to populate the
witnessUtxo field in the PSBT inputs.3
Select UTXOs
Select enough UTXOs to cover the deposit amount plus estimated fees.
4
Build the PSBT
Create a PSBT with:
- Inputs: Selected UTXOs with witness data
- Output 1: Payment to
to_addressfor the depositamount - Output 2:
OP_RETURNwith the hex-encoded memo - Output 3 (if needed): Change back to the sender’s address
5
Estimate fees
Fetch the recommended fee rate from Mempool.space and calculate the transaction fee based on input/output count. Re-select UTXOs if the initial selection doesn’t cover the fee.
6
Sign and broadcast
Sign the PSBT with your private key and broadcast the raw transaction.
Full Example
Signing for Different Address Types
Bitcoin has several address formats, each with different signing requirements:Hardware / Browser Wallet Signing
If you’re using a wallet provider (e.g., Xverse, Unisat, Leather) instead of a raw private key, the flow changes at the signing step. Instead of signing locally, you pass the unsigned PSBT hex to the wallet’ssignPsbt method: