Order Settler API

This documentation outlines the integration flow for Order Settlers using Bolt Liquidity with synchronous settlement on Sui.

Overview

As a Order Settler, you interact with the PrivateSettlementService to provide liquidity, settle orders created by Swap Aggregators, and manage your assets across pools.

Private API Interface

service PrivateSettlementService {
  rpc SwapExactIn(SwapExactInRequest) returns (SwapExactInResponse);
  rpc WithdrawQuotes(WithdrawQuotesRequest) returns (WithdrawQuotesResponse);
  rpc WithdrawAllQuotes(WithdrawAllQuotesRequest) returns (WithdrawAllQuotesResponse);
  rpc DepositBase(DepositBaseRequest) returns (DepositBaseResponse);
  rpc WithdrawBase(WithdrawBaseRequest) returns (WithdrawBaseResponse);
  rpc WithdrawAllBases(WithdrawAllBasesRequest) returns (WithdrawAllBasesResponse);
  rpc Info(InfoRequest) returns (InfoResponse);
}

Core Components

SwapExactIn

Main entry point for executing swaps with specific input amounts.

message SwapExactInRequest {
  string want_out = 1;                  // Base asset to receive
  Balance input = 2;                    // Quote asset and amount to swap in
  string min_base_out = 3;              // Minimum base to receive
  string receiver = 4;                  // Optional different recipient
}

message SwapExactInResponse {
  Balance base_out = 1;                 // Base asset received
  string spot_price = 2;                // Price used for swap
  Balance fee = 3;                      // Fee taken
  string trade_id = 5;                  // Unique trade identifier
}

Deposit/Withdraw

Methods for managing liquidity in pools.

message DepositBaseRequest {
  Balance amount = 1;                   // Base asset and amount to deposit
}

message WithdrawBaseRequest {
  string base_asset = 1;                // Base asset to withdraw
}

message WithdrawBaseResponse {
  Balance withdrawn = 1;                // Amount withdrawn
}

Implementation Flow

1. Account Setup and Verification

// Get your account info
let info = client.info(InfoRequest {}).await?;
println!("Connected to network: {}", info.network_id);
println!("Operating as: {}", info.whoami);
// Go
resp, err := client.Info(ctx, &pb.InfoRequest{})
if err != nil {
    log.Fatalf("Could not get info: %v", err)
}
log.Printf("Connected to network: %s as %s", resp.NetworkId, resp.Whoami)

2. Providing Liquidity

// Deposit base asset to a pool
let deposit_response = client.deposit_base(
    ctx_with_funds, // Context with ETH attached
    DepositBaseRequest {
        amount: Balance {
            asset: "ETH".to_string(),
            amount: "10.0".to_string()
        }
    }
).await?;
# Python
deposit_response = await client.deposit_base(
    DepositBaseRequest(
        amount=Balance(
            asset="ETH",
            amount="10.0"
        )
    ),
    metadata=attach_funds("ETH", 10.0)  # Implementation depends on your gRPC client
)

3. Settling Orders

Market Makers listen for new orders and settle them to earn fees.

// Monitor for new orders
async fn monitor_for_orders(client: &OrderMonitorClient) -> Result<()> {
    // Subscribe to NewOrderEvent
    let mut stream = client.subscribe_events("NewOrderEvent").await?;
    
    while let Some(event) = stream.next().await {
        let event = event?;
        let order_id = event.order_id;
        
        // Get order details
        let order = client.get_order(order_id).await?;
        
        if should_settle_order(&order) {
            // Calculate settlement amount based on order
            let settlement_amount = calculate_settlement_amount(&order);
            
            // Settle the order
            client.settle_order(
                ctx_with_settlement_funds, // Context with settlement funds
                order_id
            ).await?;
            
            println!("Settled order: {}", order_id);
        }
    }
    
    Ok(())
}

fn should_settle_order(order: &Order) -> bool {
    // Implement your strategy for selecting orders to settle
    // Consider:
    // - Profitability (price compared to your sources)
    // - Risk (asset volatility)
    // - Order expiry time
    // - Asset availability in your inventory
    true
}
// JavaScript/TypeScript
async function monitorForOrders(client) {
  const stream = await client.subscribeEvents("NewOrderEvent");
  
  for await (const event of stream) {
    const orderId = event.orderId;
    const order = await client.getOrder(orderId);
    
    if (shouldSettleOrder(order)) {
      const settlementAmount = calculateSettlementAmount(order);
      
      try {
        await client.settleOrder(
          orderId,
          {
            // Attach settlement funds
            funds: {
              denom: order.want,
              amount: settlementAmount
            }
          }
        );
        
        console.log(`Settled order: ${orderId}`);
      } catch (error) {
        console.error(`Settlement failed: ${error.message}`);
      }
    }
  }
}

4. Direct Swap Execution

For direct, immediate swaps without going through the order system:

// Execute a direct swap
let swap_response = client.swap_exact_in(
    ctx_with_funds, // Context with USDC attached
    SwapExactInRequest {
        want_out: "ETH".to_string(),
        input: Balance {
            asset: "USDC".to_string(),
            amount: "1000.0".to_string()
        },
        min_base_out: "0.5".to_string(),
        receiver: None // Send to self
    }
).await?;

println!("Received: {} ETH", swap_response.base_out.amount);
println!("At price: {}", swap_response.spot_price);
println!("Fee paid: {} {}", swap_response.fee.amount, swap_response.fee.denom);

5. Collecting Quote Assets

Periodically harvest quote assets that accumulate from settled trades.

// Withdraw quotes from a single pool
let withdraw_quotes_resp = client.withdraw_quotes(
    WithdrawQuotesRequest {
        base_asset: "ETH".to_string()
    }
).await?;

for quote in withdraw_quotes_resp.withdrawn {
    println!("Withdrawn: {} {}", quote.amount, quote.denom);
}

// Or withdraw from all pools at once
let withdraw_all_resp = client.withdraw_all_quotes(
    WithdrawAllQuotesRequest {}
).await?;

for quote in withdraw_all_resp.all_withdrawn {
    println!("Withdrawn: {} {}", quote.amount, quote.denom);
}

6. LP Token Management

Market Makers must manage LP tokens for tracking pool ownership and claiming rewards.

// Deposit liquidity and receive LP tokens
let deposit_liq_resp = client.deposit_liquidity(
    ctx_with_base_funds,
    DepositLiquidityRequest {
        receiver: None, // Self
        min_lp_out: 500 // Minimum LP tokens to receive
    }
).await?;

println!("Received {} LP tokens", deposit_liq_resp.lp_out);

// Withdraw liquidity by burning LP tokens
let withdraw_liq_resp = client.withdraw_liquidity(
    WithdrawLiquidityRequest {
        receiver: None, // Self
        lps_in: 250, // LP tokens to burn (half position)
        min_out: 475 // Minimum base asset to receive
    }
).await?;

println!("Received {} base asset", withdraw_liq_resp.liquidity_out);

// Claim accumulated rewards
client.claim_rewards(
    ClaimRewardsRequest {
        to: None // Self
    }
).await?;

Price Oracle Integration

Order Settlers interact with the price oracle to update and validate prices.

// Update price with cryptographic proof
client.update_price(
    UpdatePriceRequest {
        base: "ETH".to_string(),
        quote: "USDC".to_string(),
        price_with_proof: PriceWithProof {
            price: 1825.50,
            proof: encoded_proof // Proof from oracle network
        }
    }
).await?;

// Verify a price is within bounds
client.price_in_bound(
    PriceInBoundRequest {
        base: "ETH".to_string(),
        quote: "USDC".to_string(),
        price: 1825.50
    }
).await?;

Strategy Considerations

Optimal Order Selection

fn evaluate_order_profitability(order: &Order, current_price: f64) -> bool {
    // Get current market price from your sources
    // Compare to order's limit price
    if let Some(limit_price) = order.limit_price {
        // Calculate potential profit after fees
        let offered_amount = order.offered.amount;
        let potential_settlement = offered_amount / limit_price;
        let market_value = potential_settlement * current_price;
        
        // Account for protocol and settlement fees
        let fee_rate = 0.0002; // Example fee rate
        let net_value = market_value * (1.0 - fee_rate);
        
        // Determine if profitable
        return net_value > offered_amount;
    }
    
    // No limit price, use price oracle bounds
    true
}

Balanced Inventory Management

async fn rebalance_inventory(client: &PrivateClient) -> Result<()> {
    // Get current balances across all pools
    let pools = client.get_pools().await?;
    
    for pool in pools {
        // Check if base asset needs rebalancing
        if pool.base_amount < target_min_liquidity(pool.base_asset) {
            // Add more liquidity
            client.deposit_base(
                ctx_with_funds,
                DepositBaseRequest {
                    amount: Balance {
                        asset: pool.base_asset.clone(),
                        amount: liquidity_to_add(pool.base_asset).to_string()
                    }
                }
            ).await?;
        } else if pool.base_amount > target_max_liquidity(pool.base_asset) {
            // Withdraw excess liquidity
            client.withdraw_base(
                WithdrawBaseRequest {
                    base_asset: pool.base_asset.clone()
                }
            ).await?;
        }
        
        // Withdraw accumulated quote assets
        client.withdraw_quotes(
            WithdrawQuotesRequest {
                base_asset: pool.base_asset.clone()
            }
        ).await?;
    }
    
    Ok(())
}

Error Handling

match client.settle_order(ctx, order_id).await {
    Ok(response) => {
        // Order settled successfully
        log_settlement_success(order_id, &response.completion_data);
    },
    Err(e) if e.to_string().contains("order has expired") => {
        // Skip expired orders
        log::info!("Order {} has expired", order_id);
    },
    Err(e) if e.to_string().contains("did not match order limit price") => {
        // Price moved against us
        log::warn!("Price mismatch for order {}: {}", order_id, e);
    },
    Err(e) if e.to_string().contains("price out of bounds") => {
        // Oracle price check failed
        log::warn!("Price out of bounds for order {}: {}", order_id, e);
        
        // Consider updating oracle price if you have a valid source
        update_oracle_price().await?;
    },
    Err(e) => {
        // Handle other errors
        log::error!("Settlement failed: {}", e);
    }
}

Performance Optimization

Batch Processing

// Process multiple orders in parallel
async fn process_pending_orders(client: &PrivateClient, orders: Vec<String>) -> Result<()> {
    let mut futures = Vec::new();
    
    for order_id in orders {
        let client = client.clone();
        let future = tokio::spawn(async move {
            match client.settle_order(ctx_with_funds.clone(), order_id.clone()).await {
                Ok(_) => Ok(order_id),
                Err(e) => Err((order_id, e.to_string()))
            }
        });
        
        futures.push(future);
    }
    
    let results = futures::future::join_all(futures).await;
    
    // Process results
    for result in results {
        match result {
            Ok(Ok(order_id)) => {
                println!("Successfully settled order: {}", order_id);
            },
            Ok(Err((order_id, error))) => {
                println!("Failed to settle order {}: {}", order_id, error);
            },
            Err(e) => {
                println!("Task failed: {}", e);
            }
        }
    }
    
    Ok(())
}

Security Considerations

  • Fund Security - Implement proper key management for settlement funds

  • Pricing Reliability - Use Bolt's Oracle service to ensure proper aggregate CEX pricing

  • Gas Management - Monitor and manage gas costs for settlement transactions

  • Order Validation - Verify order parameters before attempting settlement

Monitoring

async fn monitor_pool_health(client: &PrivateClient) -> Result<()> {
    // Get all pools
    let pools = client.get_pools().await?;
    
    for pool in pools {
        // Monitor liquidity relative to trading volume
        let trades = client.get_recent_trades(&pool.base_asset).await?;
        let trading_volume = calculate_24h_volume(&trades);
        
        // Calculate liquidity ratio
        let liquidity_ratio = pool.base_amount / trading_volume;
        
        if liquidity_ratio < 0.2 {  // Less than 20% of daily volume
            alert("Low liquidity in pool: {}", pool.base_asset);
        }
        
        // Monitor fee accrual
        let accrued_fees = estimate_accrued_fees(&trades);
        println!("Pool {} accrued ~{} in fees", pool.base_asset, accrued_fees);
    }
    
    Ok(())
}

Last updated