Skip to content

Instantly share code, notes, and snippets.

@adamkrellenstein
Created August 6, 2024 21:30
Show Gist options
  • Save adamkrellenstein/f1322377cb496efa4d4345cb06f1fcd9 to your computer and use it in GitHub Desktop.
Save adamkrellenstein/f1322377cb496efa4d4345cb06f1fcd9 to your computer and use it in GitHub Desktop.
Native Atomic Swaps - Protocol Specification

Native Atomic Swaps

Motivations

Current methods of exchange Counterparty assets with Bitcoin require either (1) multiple confirmations (via BTCPay on the DEx) or (2) a trusted seller (via dispensers). As noted by DerpHerpenstein,[1] the only way securely to achieve atomic, single-confirmation trades with BTC is by extending Counterparty to use the UTXO system of Bitcoin directly. It's also clear that the address-based system currently used by Counterparty is much more flexible and powerful. For example, if a person has several assets with different balances on the same UTXO, it becomes complicated to send only part of one of the assets. Another problem with both the current system and the one proposed by DerpHerpenstein is that multiple transactions are required to place or conclude an order. Finally, the management of PSBTs may require additional transactions to offer an alternative to centralized solutions.

Design

The system proposed here attempts to resolve all of these problems: It aims to enable placing and concluding a BTC sale order in a single transaction per party, only storing escrowed assets for an order in UTXOs and offering an on-chain solution to store and revoke PSBTs. It offers the possibility of opening and executing an order equally well from across addresses and UTXOs (!) This design is also the fastest to develop and least intrusive, as it requires minimal changes to the existing codebase.

N.B. The user must have two UTXOs (from the same or from two different addresses) to be able to place a sell order: the first to build the PSBT transaction that will be completed by the buyer, and the second to build the transaction to open the sell order.

Workflow for Atomic Swap

Step 1. Seller Constructs and Signs Incomplete PSBT

Transaction: Incomplete PSBT

Inputs Outputs
<utxo_1>
Output Containing Asset for Sale <payment_to_seller>
Asset Price and Seller Address
<data_a>
Asset name, Quantity, Order Expiration Block (optional)

Step 2. Seller Constructs, Signs + Broadcasts Sell Order with PSBT in Data Packet

Transaction: Sell Order

Inputs Outputs
<utxo_2>
Seller Address <data_b>
Counterparty Prefix and ID, Transaction PSBT

Counterparty Nodes will parse this transaction and expose the embedded Incomplete PSBT via its API, with a unique identifier equal to the transaction hash of the sell order.

(If assets are already attached to <utxo_1>the quantity of the sell order must be exactly equal to the quantity of attached assets; else, Counterparty nodes will record that the assets for sale are escrowed in <utxo_1>.)

Step 3. Buyer Signs PSBT

The buyer retrieves the Incomplete PSBT from the node API and completes it as follows:

Completed PSBT

Inputs Outputs
<utxo_1>
Output Containing Asset for Sale <payment_to_seller>
Asset Price, Seller Address
<payment_from_buyer>
Buyer Address <data_a>
Asset Name, Quantity, Order Expiration Block
<data_c>
Destination Type (utxo xor address)
<change_to_buyer>
Buyer Address

Upon seeing this transaction in the Bitcoin blockchain, Counterparty nodes verify:

  1. that the expiration_block has not yet passed, and
  2. that the order has not been canceled.

If these conditions are met, they release the assets escrowed in <utxo_1> and credit the buyer's address / UTXO (depending on value of the destination_type parameter).

Canceling a Sell Order

To cancel an order, simply broadcast a classic Counterparty transaction:

Inputs Outputs
Seller Address
Cancel Order Escrowed in utxo_1

Implementation

Two new contracts will be written:

  • atomicsell.py — This contract allows construction and parsing of transactions to open a BTC sell order.
  • atomicbuy.py — This contract allows construction and parsing of transactions to execute a BTC buy order.

The cancel.py contract will be modified to enable cancellation of an Atomic Swap order.

Signature of the main functions

atomicsell.py

def prepare_compose(
    db,
    source: str,
    quantity: int,
    price: int,
    expiration_block: int,
):
    """
    Returns an unsigned PSBT transaction, to be signed and used with the `signed_psbt` parameter of the compose function
    :param db: The database object.
    :param source: The source address or UTXO (`<tx_hash>:<n>`).
    :param price: The price of the assets in satoshis.
    :param expiration_block: The block height at which the order will expire.
    """

def compose(
    db,
    signed_psbt: str,
):
    """
    Return a raw transaction hex to be broadcasted.
    :param db: The database object.
    :param signed_psbt: The signed PSBT transaction returned by the `prepare_compose()` function.
    """

atomicbuy.py

def compose(
    db,
    source: str,
    order_tx_hash: str,
    destination_type: str,
):
    """
    Return a raw transaction hex to be broadcasted.
    :param db: The database object.
    :param source: The source address of the transaction.
    :param order_tx_hash: The transaction hash of the sell order.
    :param destination_type: The type of destination. Can be `address` or `utxo`.
    """

API Changes

  • New Routes
    • Get All Atomic Orders: /v2/atomicorders
    • Get Atomic Order: /v2/atomicorders/<tx_hash>
    • Get Atomic Orders by Asset: /v2/assets/<asset>/atomicorders
    • Prepare Atomic Sell: /v2/addresses/<address>/compose/atomicsell/prepare
    • Compose Atomic Sell: /v2/addresses/<address>/compose/atomicsell
    • Compose Atomic Buy: /v2/addresses/<address>/compose/atomicbuy

Database Changes

  • A new table atomicorders will be created with the following fields:
    1. tx_hash (Atomic Sell identifier)
    2. block_index
    3. source (source of the transaction)
    4. utxo
    5. asset
    6. quantity
    7. price
    8. expiration
    9. buyer (address or utxo)
    10. buy_tx_hash
    11. buy_block_index
    12. buy_source
    13. status (open, filled, canceled, expired, invalid: <reasons>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment