The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Atomic Swaps and Raw Transactions

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.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.