Implementing a cryptocurrency order exchange system with the Blnk Ledger.
This guide demonstrates how to implement a secure and efficient cryptocurrency order exchange system with the Blnk Ledger. You’ll learn how to handle order creation, escrow management, order matching, and atomic settlements.A cryptocurrency exchange operates in three main steps:
Order Creation: Users express their intent to trade by creating orders
Order Matching: Compatible orders are paired together
Settlement: Assets are exchanged atomically between parties
To illustrate this flow, let’s work with a practical example where two users want to exchange different cryptocurrencies:
Initial State:John has 1 ETH available in his accountEmily has 10 MATIC available in her accountOrder Intent:John wants to receive 10 MATIC in exchange for 1 ETHEmily wants to receive 1 ETH in exchange for 10 MATIC
Before writing code, it’s crucial to design a money movement map that outlines how money moves in your system. This serves as the blueprint for your implementation.For our order exchange system, here’s how funds will flow:
Each customer initiates their order by starting a transaction in an inflight state, temporarily holding their assets (e.g., ETH or MATIC) in escrow (@ETH_Escrow or @MATIC_Escrow) until the order is matched or finalized:
John deposits ETH into @ETH_Escrow (inflight).
Emily deposits MATIC into @MATIC_Escrow (inflight).
Order matching occurs when the system pairs John’s inflight ETH transaction in @ETH_Escrow with Customer B’s inflight MATIC transaction in @MATIC_Escrow, linking them under a shared . This step confirms compatibility and prepares the orders for settlement, keeping funds securely held in escrow until the final exchange.
John’s ETH in @ETH_Escrow matches Emily’s MATIC order (and linked by a root id: root_id_123).
Emily’s MATIC in @MATIC_Escrow matches John’s ETH order (and linked by a root id: root_id_123).
Inflight MATIC transaction to @MATIC_Escrow commits and settles to John’s MATIC balance. The settlement transactions are linked to matched orders with the root_id.
Inflight ETH transaction to @ETH_Escrow commits and settles to Emily’s ETH balance. The settlement transactions are linked to matched orders with the root_id.
Before implementing the exchange logic, we need to establish the foundational structure in your Blnk Ledger. Let’s set up separate ledgers for each cryptocurrency and create the necessary balances:
In this example, we use a precision of 100 for simplicity (e.g., 1 ETH = 100 units). In production, cryptocurrencies like ETH use a precision of 10^18 (1 ETH = 10^18 wei). Adjust the precision parameter in all transaction calls accordingly and ensure amount calculations reflect this precision.
async function recordOrder(orderReference: string, sendCurrency: string, receiveCurrency: string, amount: number) { const orderBook = { reference: orderReference, send_currency: sendCurrency, receive_currency: receiveCurrency, amount: amount, status: 'pending_match', created_at: new Date().toISOString() }; // Store in your order book system // This is a placeholder - implement your storage logic here return orderBook;}
Bringing it all together.
async function createExchangeOrder( sourceBalanceId: string, amount: number, sendCurrency: string, receiveCurrency: string) { // Step 1: Check balance const balanceCheck = await checkBalance(sourceBalanceId, amount); if (!balanceCheck.success) { return balanceCheck; } // Step 2: Create inflight transaction const { inflightTx, orderReference } = await createInflightTransaction( sourceBalanceId, amount, sendCurrency, receiveCurrency ); // Step 3: Record in order book const order = await recordOrder(orderReference, sendCurrency, receiveCurrency, amount); return { success: true, orderReference, transaction: inflightTx, order };}// Create order for Johnconst johnOrder = await createExchangeOrder( johnEthBalance.id, // John's ETH balance ID 1, // 1 ETH (with precision 100) 'ETH', // Sending ETH 'MATIC' // Receiving MATIC);// Create order for Emilyconst emilyOrder = await createExchangeOrder( emilyMaticBalance.id, // Emily's MATIC balance ID 10, // 10 MATIC 'MATIC', // Sending MATIC 'ETH' // Receiving ETH);
Add inflight_expiry_date to automatically roll back the inflight transaction if an order isn’t matched within a timeout period.
When two orders are matched (e.g., John and Emily), we want to link them together with a common matching ID. This helps us track which orders in our Ledger were matched with each other.
TypeScript
async function linkMatchedOrders(transaction1Id: string, transaction2Id: string) { const baseUrl = process.env.BLNK_BASE_URL ?? 'http://localhost:5001'; const apiKey = process.env.BLNK_API_KEY ?? ''; const matchingId = `match_${uuidv4()}`; for (const [txnId, matchedWith] of [ [transaction1Id, transaction2Id], [transaction2Id, transaction1Id], ]) { const res = await fetch(`${baseUrl}/${txnId}/metadata`, { method: 'POST', headers: { 'X-Blnk-Key': apiKey, 'Content-Type': 'application/json', }, body: JSON.stringify({ meta_data: { matching_id: matchingId, matched_with_tx: matchedWith, }, }), }); if (!res.ok) throw new Error(await res.text()); } return matchingId;}// Example usage when John and Emily's orders are matched:const matchingId = await linkMatchedOrders( johnOrder.transaction.transaction_id, // John's inflight transaction ID emilyOrder.transaction.transaction_id // Emily's inflight transaction ID);
Balance Validation: Always verify available balances before creating orders. Remember to consider both actual balances and inflight amounts to prevent over-commitment of funds.
Transaction References: Always use meaningful reference prefixes (‘order_’, ‘match_’, ‘settlement_’) combined with UUIDs. This makes it easier to track and audit transactions throughout their lifecycle.
Metadata Management: Ensure metadata consistency across all related transactions. The matching ID should flow through all transactions involved in an exchange, creating a clear chain of linked operations.
Error Handling: Implement comprehensive error handling at each step. If any part of the process fails, you need to be able to identify where the failure occurred and handle it appropriately.
Rate Limiting: Consider implementing rate limits for order creation to prevent system overload and potential abuse. See the server and security configuration docs.
Currency Precision: Different cryptocurrencies might require different precision settings. Ensure to use consistent precision value per currency in your Ledger.
Escrow Management: Regularly audit escrow accounts to ensure they zero out correctly after settlements. Any remaining balance could indicate failed or incomplete settlements.