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¶
- Understand the Aspens Markets Stack Architecture
- Set Up Your Development Environment
- Connect to an Aspens Markets Stack
- Implement Core Trading Functionality
- Build a Cross-Chain Application
- Apply Security Best Practices
- Test and Deploy Your Application
- Add Advanced Features and Optimizations
- 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:
- Your application sends requests to the AMS API layer
- The API layer processes these requests and routes them to the appropriate services
- Services interact with the relevant blockchain networks
- 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:¶
For Python:¶
For Go:¶
Generate API Client Code¶
Clone the Aspens protocol buffer definitions:
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:¶
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