Bolt API

This documentation outlines the integration interface for existing Swap Providers wanting to use the Bolt Liquidity API for immediate settlement of best pricing on Sui.

Bolt also provides a TypeScript SDK for easy integration, see Bolt SDK.

Overview

As a Swap Provider, you interact with the PublicSettlementService to discover Bolt Liquidity pools, create orders, and monitor trade execution. Trade settlement is handled by third-party Order Settlers who are monitoring incoming orders and executing at best price.

Public API Interface

Endpoint = *coming soon*

service PublicSettlementService {
  rpc GetPools(GetPoolsRequest) returns (GetPoolsResponse);
  rpc GetPool(GetPoolRequest) returns (GetPoolResponse);
  rpc GetPoolByBaseAsset(GetPoolByBaseAssetRequest) returns (GetPoolByBaseAssetResponse);
  rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse);
  rpc GetTrade(GetTradeRequest) returns (GetTradeResponse);
  rpc SubscribeTrades(SubscribeTradesRequest) returns (stream SubscribeTradesResponse);
  rpc SettlementInfo(SettlementInfoRequest) returns (SettlementInfoResponse);
}

Core Data Structures

Pool

message Pool {
  string base_asset = 1;           // The main asset managed by the pool
  string base_amount = 2;          // Total amount of base asset in pool
  repeated string quote_assets = 3; // Assets that can be used to acquire base
  string lp_fee_ratio = 4;         // Fee paid to liquidity providers
  string protocol_fee_ratio = 5;   // Fee paid to protocol
  string min_base_out = 6;         // Minimum amount of base that can be swapped
}

Trade

message Trade {
  string id = 1;                   // Unique trade identifier
  Balance input = 2;               // Input asset and amount
  Balance output = 3;              // Output asset and amount
  string source = 4;               // Source of the trade (pool ID)
  Balance fee = 5;                 // Fee paid for the trade
  string sender = 6;               // Trade initiator
  uint64 height = 7;               // Block height when trade occurred
}

Sync Settlement Flow

The Bolt API primarily uses an order-based synchronous settlement system across a range of assets per chain outpost

1. Pool Discovery

// Get all available pools
let pools_response = client.get_pools(GetPoolsRequest {}).await?;
let available_pools = pools_response.pools;

// Find a specific pool by base asset
let eth_pool_response = client.get_pool_by_base_asset(
    GetPoolByBaseAssetRequest {
        base_asset: "ETH".to_string()
    }
).await?;

let eth_pool = eth_pool_response.pool;
// TypeScript
const poolsResponse = await client.getPools({});
const availablePools = poolsResponse.pools;

const ethPoolResponse = await client.getPoolByBaseAsset({
  baseAsset: "ETH"
});
const ethPool = ethPoolResponse.pool;

2. Calculate Swap Details

// Get user balance
let balance_response = client.get_balances(
    GetBalancesRequest {
        user: "0xUSER_ADDRESS".to_string(),
        base_asset: "ETH".to_string()
    }
).await?;

// Get oracle price and calculate amounts
let price = price_oracle_client.get_price("USDC", "ETH").await?;
let input_amount = 1000.0; // 1000 USDC
let expected_output = input_amount * price.price * (1.0 - eth_pool.lp_fee_ratio - eth_pool.protocol_fee_ratio);

3. Create Order

Orders are the primary mechanism for executing trades. The order is created by the Swap Provider and settled by a third party Order Settler.

// Create order with attached funds
let create_order_resp = client.create_order(
    ctx_with_funds, // Context with 1000 USDC attached
    CreateOrderRequest {
        wanted_asset: "ETH".to_string(),
        limit_price: Some(price.price * 0.99), // 1% slippage
        expiry_time: Some(UtcTime(current_time + 300000)) // 5 min expiry
    }
).await?;

let order_id = create_order_resp.order_id;
// TypeScript
const createOrderResp = await client.createOrder(
  {
    wantedAsset: "ETH",
    limitPrice: price * 0.99, // 1% slippage
    expiryTime: Date.now() + 300000 // 5 min expiry
  },
  { funds: { denom: "USDC", amount: "1000.0" } }
);

const orderId = createOrderResp.orderId;

4. Monitor Order Status

After submitting an order via Bolt, you can track its status to determine when it's settled.

// Poll for trade status
async fn wait_for_settlement(client: &PublicClient, order_id: &str) -> Result<Trade> {
    let mut attempts = 0;
    while attempts < 10 {
        // Try to get trade info
        let resp = client.get_trade(
            GetTradeRequest {
                id: order_id.to_string()
            }
        ).await?;
        
        if let Some(trade) = resp.trade {
            return Ok(trade);
        }
        
        tokio::time::sleep(Duration::from_millis(1000)).await;
        attempts += 1;
    }
    
    Err(anyhow::anyhow!("Trade settlement timeout"))
}
// TypeScript
async function waitForSettlement(client, orderId) {
  for (let i = 0; i < 10; i++) {
    try {
      const resp = await client.getTrade({ id: orderId });
      if (resp.trade) {
        return resp.trade;
      }
    } catch (e) {
      // Handle error or continue
    }
    
    await new Promise(r => setTimeout(r, 1000));
  }
  
  throw new Error("Trade settlement timeout");
}

5. Subscribe to Trade Events

For real-time updates on trades occurring in the pools:

// Subscribe to all trade events
let mut stream = client.subscribe_trades(SubscribeTradesRequest {}).await?;

while let Some(trade_event) = stream.message().await? {
    println!("New trade: {} {} → {} {}", 
        trade_event.trade.input.amount, trade_event.trade.input.denom,
        trade_event.trade.output.amount, trade_event.trade.output.denom);
    
    // Handle the trade event (update UI, etc.)
}

Error Handling

match client.create_order(ctx, request).await {
    Ok(response) => {
        // Order created successfully
        handle_new_order(response.order_id);
    },
    Err(e) if e.to_string().contains("pair not found") => {
        // No trading pair available
        display_error("This trading pair is not supported");
    },
    Err(e) if e.to_string().contains("order already expired") => {
        // Expiry time in the past
        display_error("Invalid order expiry time");
    },
    Err(e) => {
        // Handle other errors
        log::error!("Order creation failed: {}", e);
        display_error("Failed to create order");
    }
}

Best Practices

  1. Trade Simulation - Before submitting a real order, simulating a trade on the frontend is advised

  2. Validate Assets First - Check that both assets are supported by Bolt before creating orders

  3. Handle Failed Settlements - Implement fallback logic when orders aren't settled in time

  4. Cache Pool Information - Reduce API calls by caching pool data with short TTL

Event Types

Event
Description

NewOrderEvent

Emitted when an order is created

OrderSettledEvent

Emitted when an order is successfully settled

ZapEvent

Emitted when a direct swap occurs

Last updated