Skip to content

Build Cross-Chain Applications with Aspens Markets Stack

This developer guide provides a comprehensive walkthrough for building applications that leverage the Aspens Markets Stack (AMS) for cross-chain trading functionality. Follow these steps to create applications that enable users to seamlessly trade assets across different blockchain networks.

Table of Contents

  1. Understand the Aspens Markets Stack Architecture
  2. Set Up Your Development Environment
  3. Connect to an Aspens Markets Stack
  4. Implement Core Trading Functionality
  5. Build a Cross-Chain Application
  6. Apply Security Best Practices
  7. Test and Deploy Your Application
  8. Add Advanced Features and Optimizations
  9. Access Resources and Support

1. Understand the Aspens Markets Stack Architecture

Review AMS Components

An Aspens Markets Stack consists of several key components that work together to enable cross-chain trading:

  • Orderbook Service: Manages and matches trading orders across different chains
  • Multi-Chain Contract Manager: Interfaces with smart contracts on multiple blockchains
  • Settlement Engine: Ensures secure and reliable trade execution
  • API Layer: Exposes functionality to applications via gRPC interfaces

Learn the API Communication Flow

Understand this communication flow for effective integration:

  1. Your application sends requests to the AMS API layer
  2. The API layer processes these requests and routes them to the appropriate services
  3. Services interact with the relevant blockchain networks
  4. Results flow back through the same path to your application

Explore Available Endpoints

Use these API endpoints in your application:

  • Market data (orderbook, trades, prices)
  • Account management (balances, deposits, withdrawals)
  • Trade execution (create, cancel, query orders)
  • Chain status information

2. Set Up Your Development Environment

Check Prerequisites

Before you begin, ensure you have:

  • A development environment with Node.js (v14+), Python (v3.8+), or Go (v1.16+)
  • Git for version control
  • gRPC tools for your preferred language
  • Access to an Aspens Markets Stack instance (public or self-hosted)

Install Required Libraries

For Node.js:

npm install @aspens/client grpc @grpc/proto-loader

For Python:

pip install aspens-client grpcio grpcio-tools

For Go:

go get github.com/aspensprotocol/aspens-sdk

Generate API Client Code

Clone the Aspens protocol buffer definitions:

git clone https://github.com/aspensprotocol/proto.git

Generate client code for your language:

For Node.js:

npx grpc_tools_node_protoc \
    --js_out=import_style=commonjs,binary:./src \
    --grpc_out=grpc_js:./src \
    --proto_path=./proto \
    ./proto/aspens.proto

For Python:

python -m grpc_tools.protoc \
    --python_out=./src \
    --grpc_python_out=./src \
    --proto_path=./proto \
    ./proto/aspens.proto

For Go:

protoc --go_out=./src --go-grpc_out=./src \
    --proto_path=./proto \
    ./proto/aspens.proto

3. Connect to an Aspens Markets Stack

Configure Connection Parameters

Create a configuration file for your application:

{
  "api": {
    "endpoint": "api.aspens-stack.example.com:50051",
    "useTls": true,
    "timeout": 5000
  },
  "credentials": {
    "apiKey": "your-api-key"
  }
}

Establish a Connection

Node.js Example:

const grpc = require('@grpc/grpc-js');
const { AspensClient } = require('./src/aspens_grpc_pb');

function createClient(config) {
  let credentials;
  if (config.api.useTls) {
    credentials = grpc.credentials.createSsl();
  } else {
    credentials = grpc.credentials.createInsecure();
  }

  const client = new AspensClient(
    config.api.endpoint,
    credentials
  );

  // Add API key metadata
  const metadata = new grpc.Metadata();
  metadata.add('x-api-key', config.credentials.apiKey);

  return { client, metadata };
}

const { client, metadata } = createClient(require('./config.json'));

Python Example:

import grpc
from src import aspens_pb2_grpc

def create_client(config):
    if config['api']['useTls']:
        credentials = grpc.ssl_channel_credentials()
        channel = grpc.secure_channel(config['api']['endpoint'], credentials)
    else:
        channel = grpc.insecure_channel(config['api']['endpoint'])

    client = aspens_pb2_grpc.AspensStub(channel)

    # Create metadata for API key
    metadata = [('x-api-key', config['credentials']['apiKey'])]

    return client, metadata

# Load config and create client
import json
with open('config.json') as f:
    config = json.load(f)

client, metadata = create_client(config)

Test the Connection

Verify your connection by requesting basic market data:

// Node.js
const { MarketRequest } = require('./src/aspens_pb');

function testConnection() {
  const request = new MarketRequest();
  request.setMarketId('USDT:USDT');

  client.getMarketInfo(request, metadata, (err, response) => {
    if (err) {
      console.error('Connection error:', err);
      return;
    }
    console.log('Connected successfully!');
    console.log('Market info:', response.toObject());
  });
}

testConnection();

4. Implement Core Trading Functionality

Fetch Market Data

Retrieve the Orderbook:

// Node.js
const { OrderbookRequest } = require('./src/aspens_pb');

function getOrderbook(marketId, depth = 10) {
  return new Promise((resolve, reject) => {
    const request = new OrderbookRequest();
    request.setMarketId(marketId);
    request.setDepth(depth);

    client.getOrderbook(request, metadata, (err, response) => {
      if (err) {
        reject(err);
        return;
      }

      resolve({
        bids: response.getBidsList().map(order => ({
          price: order.getPrice(),
          quantity: order.getQuantity()
        })),
        asks: response.getAsksList().map(order => ({
          price: order.getPrice(),
          quantity: order.getQuantity()
        }))
      });
    });
  });
}

Manage User Balances

Get User Balances:

# Python
from src import aspens_pb2

def get_balances(user_id):
    request = aspens_pb2.BalanceRequest()
    request.user_id = user_id

    response = client.GetBalances(request, metadata=metadata)

    balances = {}
    for balance in response.balances:
        balances[balance.token] = {
            'available': balance.available,
            'locked': balance.locked
        }

    return balances

Place and Manage Orders

Create a Limit Order:

// Node.js
const { OrderRequest, OrderType, OrderSide } = require('./src/aspens_pb');

function placeLimitOrder(marketId, userId, side, price, quantity) {
  return new Promise((resolve, reject) => {
    const request = new OrderRequest();
    request.setMarketId(marketId);
    request.setUserId(userId);
    request.setOrderType(OrderType.LIMIT);
    request.setOrderSide(side === 'buy' ? OrderSide.BUY : OrderSide.SELL);
    request.setPrice(price);
    request.setQuantity(quantity);

    client.placeOrder(request, metadata, (err, response) => {
      if (err) {
        reject(err);
        return;
      }

      resolve({
        orderId: response.getOrderId(),
        status: response.getStatus()
      });
    });
  });
}

Cancel an Order:

# Python
from src import aspens_pb2

def cancel_order(order_id, user_id):
    request = aspens_pb2.CancelOrderRequest()
    request.order_id = order_id
    request.user_id = user_id

    response = client.CancelOrder(request, metadata=metadata)

    return {
        'success': response.success,
        'message': response.message
    }

5. Build a Cross-Chain Application

Create a Cross-Chain Trading Dashboard

Implement a simple web application that enables users to trade USDT on Ethereum and USDC on Hedera.

Organize Backend Structure:

src/
├── api/
│   ├── routes.js        # Express routes
│   └── controllers.js   # Business logic
├── services/
│   ├── aspens.js        # AMS client wrapper
│   ├── ethereum.js      # Ethereum wallet integration
│   └── hedera.js        # Hedera wallet integration
├── utils/
│   └── validation.js    # Input validation
└── server.js            # Main entry point

Develop Frontend Components:

src/
├── components/
│   ├── Orderbook.jsx      # Displays orderbook data
│   ├── TradeForm.jsx      # Order entry form
│   ├── WalletConnect.jsx  # Wallet connection UI
│   └── TradeHistory.jsx   # Shows recent trades
├── services/
│   └── api.js             # API client for backend
└── App.jsx                # Main application

Integrate Wallets

Implement wallet connections for both chains:

// ethereum.js
const { ethers } = require('ethers');

async function connectEthereumWallet() {
  if (window.ethereum) {
    try {
      // Request account access
      const accounts = await window.ethereum.request({ 
        method: 'eth_requestAccounts' 
      });

      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();

      return {
        address: accounts[0],
        provider,
        signer
      };
    } catch (error) {
      throw new Error(`Ethereum wallet connection failed: ${error.message}`);
    }
  } else {
    throw new Error('Please install MetaMask to use this feature');
  }
}

Create Order Submission Functionality

Create a controller that handles trade submissions:

// controllers.js
const aspensService = require('../services/aspens');

async function submitTrade(req, res) {
  try {
    const { marketId, side, price, quantity, userId } = req.body;

    // Validate inputs
    if (!marketId || !side || !price || !quantity || !userId) {
      return res.status(400).json({ 
        success: false, 
        message: 'Missing required parameters' 
      });
    }

    // Submit order to Aspens Markets Stack
    const orderResult = await aspensService.placeLimitOrder(
      marketId, 
      userId, 
      side, 
      price, 
      quantity
    );

    return res.json({
      success: true,
      data: orderResult
    });
  } catch (error) {
    console.error('Trade submission error:', error);
    return res.status(500).json({
      success: false,
      message: error.message || 'Failed to submit trade'
    });
  }
}

Add Real-Time Updates with WebSockets

Implement real-time orderbook updates:

// aspens.js
const WebSocket = require('ws');

function subscribeToOrderbook(marketId, callback) {
  const ws = new WebSocket('wss://api.aspens-stack.example.com/ws');

  ws.on('open', () => {
    ws.send(JSON.stringify({
      action: 'subscribe',
      channel: 'orderbook',
      marketId
    }));
  });

  ws.on('message', (data) => {
    try {
      const parsedData = JSON.parse(data);
      callback(null, parsedData);
    } catch (error) {
      callback(error);
    }
  });

  ws.on('error', (error) => {
    callback(error);
  });

  return {
    unsubscribe: () => {
      ws.close();
    }
  };
}

6. Apply Security Best Practices

Protect User Data

  • Always use TLS for API connections
  • Never store API keys or private keys in client-side code
  • Implement proper authentication and authorization

Handle Transactions Securely

  • Validate all inputs before submitting to the API
  • Implement transaction signing on the client side only
  • Provide clear transaction previews before user confirmation

Implement Robust Error Handling

Use this pattern for robust error handling:

async function safeApiCall(apiFunction, ...args) {
  try {
    return await apiFunction(...args);
  } catch (error) {
    // Log the error (to your monitoring service)
    console.error(`API call failed: ${error.message}`);

    // Classify errors for better user experience
    if (error.code === 'UNAVAILABLE') {
      throw new Error('Service temporarily unavailable. Please try again later.');
    } else if (error.code === 'PERMISSION_DENIED') {
      throw new Error('Authentication failed. Please reconnect your wallet.');
    } else {
      throw new Error('An unexpected error occurred. Please try again.');
    }
  }
}

7. Test and Deploy Your Application

Write Unit Tests

Create tests for your API integration:

// test/aspens.test.js
const { expect } = require('chai');
const sinon = require('sinon');
const aspensService = require('../src/services/aspens');

describe('Aspens Market Stack Integration', () => {
  it('should fetch orderbook data successfully', async () => {
    // Mock response data
    const mockOrderbook = {
      bids: [{ price: '0.995', quantity: '1000' }],
      asks: [{ price: '1.005', quantity: '500' }]
    };

    // Create a stub for the API call
    const stub = sinon.stub(aspensService.client, 'getOrderbook')
      .callsFake((request, metadata, callback) => {
        callback(null, { 
          getBidsList: () => mockOrderbook.bids.map(bid => ({
            getPrice: () => bid.price,
            getQuantity: () => bid.quantity
          })),
          getAsksList: () => mockOrderbook.asks.map(ask => ({
            getPrice: () => ask.price,
            getQuantity: () => ask.quantity
          }))
        });
      });

    // Call the service method
    const result = await aspensService.getOrderbook('USDT:USDT');

    // Verify the result
    expect(result.bids).to.have.lengthOf(1);
    expect(result.asks).to.have.lengthOf(1);
    expect(result.bids[0].price).to.equal('0.995');

    // Restore the stub
    stub.restore();
  });
});

Run Integration Tests

Test with a sandbox AMS instance:

// Set up environment for testing
process.env.ASPENS_API_ENDPOINT = 'sandbox.aspens-stack.example.com:50051';
process.env.ASPENS_API_KEY = 'test-api-key';

// Run your integration tests
// ...

Deploy Your Application

Deploy your application with proper environment configuration:

# Example deployment script for a Node.js application
npm run build

# Set production environment variables
export NODE_ENV=production
export ASPENS_API_ENDPOINT=api.aspens-stack.example.com:50051
export ASPENS_API_KEY=your-production-api-key

# Start the application
npm start

8. Add Advanced Features and Optimizations

Optimize Performance

  • Implement connection pooling for gRPC clients
  • Cache frequently accessed market data
  • Use pagination for large result sets

Add Advanced Trading Features

  • Implement advanced order types (stop-loss, take-profit)
  • Add portfolio tracking and analytics
  • Create trading bots using the API

Develop Multi-Chain Wallet Management

Help users manage assets across chains:

async function suggestCrossChainOpportunities(userId) {
  // Get balances on both chains
  const balances = await aspensService.getBalances(userId);

  // Get current market rates
  const market = await aspensService.getMarketInfo('USDT:USDT');

  // Calculate potential arbitrage opportunities
  const opportunities = [];

  if (balances['USDT-ethereum'] > 100 && market.lastPrice < 0.98) {
    opportunities.push({
      type: 'buy',
      from: 'Ethereum',
      to: 'Hedera',
      potentialProfit: `${((1/market.lastPrice - 1) * 100).toFixed(2)}%`
    });
  }

  if (balances['USDT-hedera'] > 100 && market.lastPrice > 1.02) {
    opportunities.push({
      type: 'sell',
      from: 'Hedera',
      to: 'Ethereum',
      potentialProfit: `${((market.lastPrice - 1) * 100).toFixed(2)}%`
    });
  }

  return opportunities;
}

9. Access Resources and Support

Read API Reference Documentation

Comprehensive API documentation is available at: - https://docs.aspens.xyz/api-reference

Explore Sample Applications

Check out complete example applications: - Cross-Chain Trading Dashboard - Arbitrage Bot - Mobile Wallet Integration

Join the Community

Connect with the Aspens developer community: - Discord: https://discord.gg/vC6ZtAPFDE - Developer Forum: https://github.com/aspensprotocol/feedback/discussions