Skip to content

Build crosschain Applications with Aspens Market Stack

This guide shows how to build applications using the Aspens Market Stack (AMS) for crosschain trading. Follow these steps to create applications that let users trade assets across blockchains.

Table of Contents

  1. Understand the Aspens Market Stack Architecture
  2. Set Up Your Development Environment
  3. Connect to an Aspens Market Stack
  4. Implement Core Trading Functionality
  5. Build a crosschain Application
  6. Test and Deploy Your Application
  7. Access Resources and Support

1. Understand the Aspens Market Stack Architecture

Review AMS Components

An Aspens Market Stack has several components that work together to enable crosschain trading:

  • Orderbook: Manage and match trading orders across different chains
  • Multi-Chain Contract Manager: Interfaces with smart contracts on multiple blockchains
  • Settlement Engine: Ensure secure trade execution
  • API Layer: Expose functionality to applications via a well-defined API

Learn the API Communication Flow

Here's the communication flow:

  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 Market Stack instance (public or self-hosted)

3. Connect to an Aspens Market 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 crosschain Application

Create a crosschain Trading Dashboard

Build a web application that lets users trade USDT on Ethereum and USDC on Arbitrum.

Organize Backend Structure:

src/
├── api/
│   ├── routes.js        # Express routes
│   └── controllers.js   # Business logic
├── services/
│   ├── aspens.js        # AMS client wrapper
│   ├── ethereum.js      # Ethereum wallet integration
│   └── arbitrum.js      # Arbitrum 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 Market 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. 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

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: 'Arbitrum',
      potentialProfit: `${((1/market.lastPrice - 1) * 100).toFixed(2)}%`
    });
  }

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

  return opportunities;
}

7. Access Resources and Support

Read API Reference Documentation

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

Explore Sample Applications

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

Join the Community

Connect with the Aspens developer community: - Telegram: https://t.me/aspens_xyz - Developer Forum: https://github.com/aspensprotocol/feedback/discussions