Skip to contents

Introduction

In a MultiChain network, atomic swaps are performed using a sequence of “raw exchange” commands. This process involves locking a transaction output (UTXO), creating a partial transaction offer, and having a counterparty complete and broadcast that transaction.

library(multichainr)

# Set the path to your MultiChain binaries
mc_set_path(Sys.getenv("MULTICHAIN_PATH"))

1. Node Initialization

We start by setting up our environment and a temporary blockchain.

chain_name <- "swap_demo_chain"

# Create and start the node
mc_node_init(chain_name)
mc_node_start(chain_name)

# Wait for the node to initialize
Sys.sleep(3) 

# Connect to the local node
config <- mc_get_config(chain_name)
conn <- mc_connect(config)

2. Preparing the Trade

To demonstrate a swap, we need two participants and two different assets.

# Create addresses for two traders
trader_a <- mc_get_new_address(conn)
trader_b <- mc_get_new_address(conn)

# Grant necessary permissions
mc_grant(conn, trader_a, "receive,send,issue")
mc_grant(conn, trader_b, "receive,send,issue")

# Issue 'AssetA' to Trader A and 'AssetB' to Trader B
mc_issue(conn, trader_a, "AssetA", 100)
mc_issue(conn, trader_b, "AssetB", 100)

# Verify balances
print(mc_get_address_balances(conn, trader_a))
print(mc_get_address_balances(conn, trader_b))

3. Step 1: Trader A Creates an Offer

Trader A wants to exchange 10 units of AssetA for 5 units of AssetB. First, Trader A must “lock” their AssetA to prepare it for the exchange.

# 1. Prepare and lock the output (10 AssetA)
# This creates a specific UTXO that can only be spent in an exchange
lock_res <- mc_prepare_lock_unspent_from(conn, 
                                        from_address = trader_a, 
                                        amounts = list(AssetA = 10))

# 2. Create the raw exchange offer
# Trader A specifies what they are giving (lock_res) and what they want (5 AssetB)
offer_hex <- mc_create_raw_exchange(conn, 
                                    txid = lock_res$txid, 
                                    vout = lock_res$vout, 
                                    amounts = list(AssetB = 5))

# The 'offer_hex' is a partial transaction string that Trader A can send 
# to Trader B via any communication channel (email, chat, etc.)
print(offer_hex)

4. Step 2: Trader B Completes the Swap

Trader B receives the offer_hex, inspects it, and decides to accept it by providing the requested 5 units of AssetB.

# 1. Trader B inspects the offer to ensure it is fair
decoded_offer <- mc_decode_raw_exchange(conn, offer_hex)
print(decoded_offer)

# 2. Trader B completes the exchange using their own funds
# This requires providing an output of 5 AssetB
# Here, we let MultiChain automatically find the best UTXO for Trader B
complete_res <- mc_prepare_lock_unspent_from(conn, 
                                             from_address = trader_b, 
                                             amounts = list(AssetB = 5))

final_tx_hex <- mc_complete_raw_exchange(conn, 
                                         tx_hex = offer_hex, 
                                         txid = complete_res$txid, 
                                         vout = complete_res$vout, 
                                         amounts = list(AssetA = 10))

5. Step 3: Broadcasting the Transaction

The exchange is now a fully formed raw transaction. It must be broadcast to the network to be included in a block.

# Broadcast the finalized transaction
swap_txid <- mc_send_raw_transaction(conn, final_tx_hex)

# Confirm the swap by checking balances
# Trader A should have +5 AssetB, Trader B should have +10 AssetA
Sys.sleep(1) # Wait for mempool processing
print(mc_get_address_balances(conn, trader_a))
print(mc_get_address_balances(conn, trader_b))

6. Cleanup

Finally, we stop the node and remove the temporary data directory.

mc_node_stop(conn)
Sys.sleep(2)

if (.Platform$OS.type == "windows") {
  base_dir <- file.path(Sys.getenv("APPDATA"), "MultiChain")
} else if (Sys.info()["sysname"] == "Darwin") {
  base_dir <- file.path(Sys.getenv("HOME"), "Library/Application Support/MultiChain")
} else {
  base_dir <- file.path(Sys.getenv("HOME"), ".multichain")
}

chain_dir <- file.path(base_dir, chain_name)
if (dir.exists(chain_dir)) {
  unlink(chain_dir, recursive = TRUE)
}

Summary

In this vignette, we demonstrated how to:

  1. Prepare Locked Outputs: Using mc_prepare_lock_unspent_from to set aside specific assets for trade.
  2. Create Offers: Generating a partial transaction hex with mc_create_raw_exchange.
  3. Audit Offers: Using mc_decode_raw_exchange to verify the contents of a trade before signing.
  4. Complete and Finalize: Combining multiple parties’ inputs into a single transaction with mc_complete_raw_exchange.
  5. Execution: Broadcasting raw hex to the blockchain using mc_send_raw_transaction.