@basedone/miniapp-sdk
v1.2.2
Published
SDK for building mini-apps for the Based One trading terminal
Downloads
179
Readme
Based.One MiniApp SDK
Official SDK for building mini-apps that integrate with the Based.One trading terminal. This SDK provides a robust postMessage-based communication layer with full TypeScript support and React integration.
✨ Features
- 🚀 Core SDK - Powerful client for terminal communication via postMessage
- ⚛️ React Integration - Custom hooks and context provider for React apps
- 💪 TypeScript First - Full type safety with comprehensive definitions
- 🔒 Permission Management - Request and manage permissions for terminal features
- 📡 Real-time Data - EventEmitter-based updates for market data, positions, and balances
- 🎨 Theme Support - Automatic theme synchronization with the terminal
- 📚 Command System - Type-safe command execution with response handling
🚀 Quick Start
Installation
npm install @basedone/miniapp-sdk
# or
yarn add @basedone/miniapp-sdkCore API (Vanilla JavaScript)
import { MiniAppSDK } from '@basedone/miniapp-sdk';
// Initialize client
const client = new MiniAppSDK({
appId: 'my-trading-app',
debug: true
});
// Wait for connection
client.on('connected', ({ sessionId, permissions }) => {
console.log('Connected!', sessionId);
});
// Place an order with automatic permission handling
try {
const response = await client.placeOrder({
symbol: 'ETH',
side: 'buy',
orderType: 'market',
size: 0.1,
// Optional: Add TP/SL (Take Profit/Stop Loss)
tpsl: {
tpPrice: 2500, // Take profit at $2500
slPrice: 1800 // Stop loss at $1800
}
});
console.log('Order placed:', response.data.orderId);
} catch (error) {
console.error('Order failed:', error.message);
}
// Subscribe to market data
await client.subscribeToMarkets(['ETH', 'BTC', 'SOL']);
client.on('market.price', (data) => {
console.log(`${data.symbol}: $${data.price}`);
});React Hooks
import React from 'react';
import {
MiniAppProvider,
useMiniApp,
useCommand,
useMarketData
} from '@basedone/miniapp-sdk/react';
function App() {
return (
<MiniAppProvider config={{
appId: 'my-trading-app',
debug: true
}}>
<TradingWidget />
</MiniAppProvider>
);
}
function TradingWidget() {
const { client, connected, sessionId } = useMiniApp();
const { execute: placeOrder, loading } = useCommand('order.submit');
const marketData = useMarketData(['ETH', 'BTC', 'SOL']);
const handleQuickBuy = async (symbol: string) => {
try {
const result = await placeOrder({
symbol,
side: 'buy',
orderType: 'market',
size: 0.1
});
alert(`Order placed: ${result.orderId}`);
} catch (err) {
alert(`Order failed: ${err.message}`);
}
};
if (!connected) return <div>Connecting...</div>;
return (
<div>
<h2>🚀 Trading Widget</h2>
<p>Session: {sessionId}</p>
{Object.entries(marketData).map(([symbol, data]) => (
<div key={symbol} style={{ margin: '10px 0' }}>
<strong>{symbol}:</strong> ${data.price?.toFixed(2)}
<button
onClick={() => handleQuickBuy(symbol)}
disabled={loading}
style={{ marginLeft: '10px' }}
>
{loading ? 'Buying...' : `Buy 0.1 ${symbol}`}
</button>
</div>
))}
</div>
);
}React Context Provider
import React from 'react';
import { MiniAppProvider, useMiniApp } from '@basedone/miniapp-sdk/react';
function App() {
const config = {
appId: 'my-trading-app',
name: 'Trading Dashboard',
url: window.location.origin,
permissions: ['read_market_data', 'place_orders'],
autoConnect: true,
debug: true
};
return (
<MiniAppProvider config={config}>
<TradingDashboard />
</MiniAppProvider>
);
}
function TradingDashboard() {
const { client, connected, sessionId, permissions } = useMiniApp();
const [marketData, setMarketData] = React.useState<Record<string, any>>({});
React.useEffect(() => {
if (!client || !connected) return;
// Subscribe to market data (market.price)
client.subscribeToMarkets(['ETH', 'BTC', 'SOL']);
// Listen for market updates
const handleMarketUpdate = (data: any) => {
setMarketData(prev => ({ ...prev, [data.symbol]: data }));
};
client.on('market.price', handleMarketUpdate);
return () => {
client.off('market.price', handleMarketUpdate);
};
}, [client, connected]);
const handleQuickBuy = async (symbol: string) => {
if (!client?.hasPermission('place_orders')) {
alert('Trading permission required');
return;
}
try {
const result = await client.placeOrder({
symbol,
side: 'buy',
orderType: 'market',
size: 0.1
});
alert(`Order placed successfully`);
} catch (err) {
alert(`Order failed: ${err.message}`);
}
};
if (!connected) return <div>Connecting...</div>;
return (
<div>
<h1>🏗️ Trading Dashboard</h1>
<p>Session: {sessionId}</p>
<p>Permissions: {Array.from(permissions).join(', ')}</p>
<div>
{Object.entries(marketData).map(([symbol, data]) => (
<div key={symbol}>
<strong>{symbol}:</strong> ${data.price?.toFixed(2)}
{client?.hasPermission('place_orders') && (
<button onClick={() => handleQuickBuy(symbol)}>
Quick Buy
</button>
)}
</div>
))}
</div>
</div>
);
}📚 API Reference
Core SDK Client
import { MiniAppSDK } from '@basedone/miniapp-sdk';
const client = new MiniAppSDK({
appId: string; // Your app identifier
name?: string; // App display name
url?: string; // App URL
debug?: boolean; // Enable console logging
autoConnect?: boolean; // Auto-connect on initialization (default: true)
targetOrigin?: string; // PostMessage target origin (default: '*')
permissions?: AppPermission[]; // Initial permissions to request
version?: string; // App version
appSecret?: string; // Optional app secret for message signing
});
// Connection Management
client.connect(): Promise<void>
client.disconnect(): void
client.getConnectionState(): ConnectionState
// Permission Management
client.hasPermission(permission: AppPermission): boolean
client.requestPermissions(permissions: AppPermission[]): Promise<AppPermission[]>
// Command Execution
client.sendCommand<T extends CommandType>(
commandName: T,
data: CommandPayload<T>
): Promise<CommandResponse<T>>
// Subscriptions
client.subscribe(
dataType: MiniAppSubscriptionType,
payload?: SubcriptionPayload
): Promise<{ subscriptionId: string; unsubscribe: () => Promise<void> }>
client.unsubscribe(subscriptionId: string): Promise<void>
// Trading Operations (convenience methods)
client.placeOrder(order: SubmitOrderCommand): Promise<CommandResponse<"order.submit">>
client.placeOrders(orders: SubmitOrderCommand[]): Promise<CommandResponse<"order.submitMultiple">>
client.cancelOrder(orderId: string, symbol?: string): Promise<CommandResponse<"order.cancel">>
client.cancelOrders(orders: CancelOrderCommand[]): Promise<CommandResponse<"order.cancelMultiple">>
client.getAccount(): Promise<CommandResponse<"wallet.balance">>
client.getPositions(symbol?: string): Promise<CommandResponse<"position.query">>
client.getOrders(symbol?: string, status?: "open" | "filled" | "cancelled" | "partial" | "all"): Promise<CommandResponse<"orders.query">>
client.getWalletAddress(): Promise<string>
// Market Data
client.subscribeToMarkets(symbols: string[]): Promise<{
subscriptionId: string;
unsubscribe: () => Promise<void>
}>
// Theme Management
client.applyTheme(): Promise<void>
client.getThemeManager(): ThemeManager
// Event Handling
client.on(event: ExtendedEventType, handler: Function): void
client.off(event: ExtendedEventType, handler: Function): void
client.emit(event: ExtendedEventType, data: any): void
// Cleanup
client.destroy(): voidReact Hooks
// Core Hooks
useMiniApp() // Access client and connection state
useCommand<T>(commandType: T) // Execute typed commands
useTheme() // Theme management
useMarketData(symbols: string[]) // Subscribe to market data
usePositions(sdk: MiniAppSDK) // Track positions
useEvent(sdk, eventType, handler) // Subscribe to events
useWalletAddress() // Get connected wallet address
// Hook Return Types
useMiniApp(): {
client: MiniAppSDK | null;
connected: boolean;
sessionId: string | undefined;
permissions: Set<AppPermission>;
error: Error | null;
}
useCommand<T>(): {
execute: (data: CommandPayload<T>) => Promise<CommandResponse>;
loading: boolean;
error: Error | null;
response: CommandResponse | null;
}
useTheme(): {
theme: Theme | null;
applyTheme: () => Promise<void>;
loading: boolean;
}
useMarketData(): Record<string, any>
usePositions(): {
positions: any[];
refresh: () => Promise<void>;
loading: boolean;
}
useWalletAddress(): {
address: string | null;
loading: boolean;
error: Error | null;
refresh: () => Promise<void>;
}React Provider
// Context Provider
<MiniAppProvider config={MiniAppConfig}>
{children}
</MiniAppProvider>
// Provider Configuration
interface MiniAppConfig {
appId: string; // Unique app identifier
name?: string; // App display name
url?: string; // App URL
targetOrigin?: string; // PostMessage target (default: '*')
permissions?: AppPermission[]; // Initial permissions
autoConnect?: boolean; // Auto-connect (default: true)
version?: string; // App version
debug?: boolean; // Debug mode
appSecret?: string; // Optional app secret
sessionId?: string; // Session ID (managed internally)
tenantId?: string; // Tenant ID for theming
}
// Context Value
interface MiniAppContextValue {
client: MiniAppSDK | null;
connected: boolean;
sessionId: string | undefined;
permissions: Set<AppPermission>;
error: Error | null;
}🔒 Permission Types
type AppPermission =
// Market data permissions
| 'read_market_data' // Access market prices and data
| 'read_orderbook' // Access order book data
| 'read_candles' // Access candlestick data
// Account permissions
| 'read_account' // Access account information
| 'read_positions' // Access position data
| 'read_orders' // Access order data
| 'read_balance' // Access balance information
| 'read_trades' // Access trade history
// Trading permissions
| 'place_orders' // Place new orders
| 'cancel_orders' // Cancel existing orders
| 'modify_orders' // Modify existing orders
| 'populate_orders' // Populate order form inputs
// UI permissions
| 'read_navigation' // Read current navigation state
| 'write_navigation' // Change navigation/symbol
| 'write_chart' // Draw on charts
| 'send_notifications' // Send notifications to user
// User settings
| 'read_user_settings' // Access user settings
| 'modify_user_settings' // Modify user settings
// Advanced permissions
| 'execute_strategies' // Execute trading strategies
| 'access_analytics'; // Access analytics data📊 Type Definitions
// Connection State
interface ConnectionState {
connected: boolean;
sessionId?: string;
permissions: Set<AppPermission>;
lastHeartbeat?: number;
}
// Order Types
interface SubmitOrderCommand {
symbol: string;
side: 'buy' | 'sell';
orderType: 'market' | 'limit' | 'stop' | 'stop_limit';
size?: number;
price?: number;
stopPrice?: number;
leverage?: number;
reduceOnly?: boolean;
postOnly?: boolean;
// TP/SL (Take Profit/Stop Loss) options
tpsl?: {
tpPrice?: number | null; // Take profit price
tpGainPercent?: number | null; // TP as percentage gain
slPrice?: number | null; // Stop loss price
slLossPercent?: number | null; // SL as percentage loss
};
}
// Order (returned from getOrders)
interface Order {
orderId: string;
clientOrderId?: string;
symbol: string;
side: 'buy' | 'sell';
orderType: 'market' | 'limit' | 'stop' | 'stop_limit';
price?: number;
stopPrice?: number;
size: number;
filledSize: number;
remainingSize: number;
status: 'open' | 'partial' | 'cancelled' | 'filled';
createdAt: number;
updatedAt: number;
tpsl?: {
tpPrice?: number;
slPrice?: number;
};
}
// Query Orders Response
interface QueryOrdersResponse {
orders: Order[];
totalCount: number;
}
// Event Types
type ExtendedEventType =
| EventType // All terminal events
| 'connected' // Connection established
| 'disconnected' // Connection lost
| 'error'; // Error occurred
type EventType =
// Market events
| 'market.ticker' // Market ticker update
| 'market.bbo' // Best bid/offer update
| 'market.prices' // Price updates for multiple symbols
| 'market.trades' // Trade executed
// Order & Position events
| 'order.status' // Order status change
| 'order.update' // Order update
| 'position.update' // Position changed
// Wallet events
| 'wallet.update' // Balance changed
| 'wallet.switch' // Wallet switched
// UI events
| 'navigation.update' // Navigation/symbol changed
| 'theme.change' // Theme changed
// Auth & Permission events
| 'auth.status' // Auth status changed
| 'permission.change' // Permissions changed
| 'permission.granted' // New permission granted
// System events
| 'connection.status' // Connection status changed
| 'notification'; // Notification received
// Command Types
type CommandType =
// Order commands
| 'order.populate' // Populate order form inputs
| 'order.submit' // Submit new order
| 'order.cancel' // Cancel existing order
| 'order.modify' // Modify existing order
// Leverage commands
| 'leverage.query' // Get current leverage settings for a user and symbol
| 'leverage.update' // Update leverage and margin modes
// Position commands
| 'position.query' // Query positions
| 'position.close' // Close position
// Wallet commands
| 'wallet.balance' // Get wallet balance
| 'wallet.address' // Get connected wallet address
// Notification commands
| 'notification.success' // Show success notification
| 'notification.error' // Show error notification
| 'notification.warning' // Show warning notification
| 'notification.info' // Show info notification
// Navigation commands
| 'navigation.navigate' // Navigate to symbol
| 'navigation.status' // Get current navigation
// Chart commands
| 'chart.draw' // Draw on chart
// Analytics commands
| 'analytics.query'; // Query analytics data
// Subscription Types
enum MiniAppSubscriptionType {
MARKET_PRICES = 'market.prices', // Price updates
BEST_BID_OFFER = 'market.bbo', // Best bid/offer
ORDER_UPDATES = 'order.update', // Order updates
POSITION_UPDATES = 'position.update', // Position updates
TRADE_UPDATES = 'trade.update', // Trade updates
ACCOUNT_UPDATES = 'account.update', // Account updates
USER_SETTINGS = 'user.settings', // User settings
NAVIGATION_UPDATES = 'navigation.update' // Navigation updates
}
// Order Response Types with Discriminated Unions
interface CommandResponse<T extends CommandType> {
success: boolean;
data?: CommandResponsePayload<T>;
error?: string;
timestamp?: number;
}
// Order Status Types (Discriminated Union)
type OrderStatus =
| { resting: { oid: string } } // Order in orderbook
| { filled: { oid: string; totalSz: string; avgPx: string } } // Order filled
| { error: string }; // Order failed
// Single Order Response
interface SubmitOrderResponse {
cloid: string; // Client order ID
statuses: [OrderStatus]; // Always array with 1 element
}
// Batch Order Result (Discriminated Union)
type BatchOrderResult =
| {
index: number;
success: true;
status: { resting: { oid: string } } | { filled: { oid: string; totalSz: string; avgPx: string } };
}
| {
index: number;
success: false;
status: { error: string };
};
// Batch Orders Response
interface SubmitMultipleOrdersResponse {
results: BatchOrderResult[];
successCount: number;
failureCount: number;
}
// Cancel Order Types
interface CancelOrderCommand {
orderId: string;
symbol?: string;
}
// Cancel Status Types (Discriminated Union)
type CancelStatus =
| { success: { oid: string } } // Cancellation succeeded
| { error: string }; // Cancellation failed
// Batch Cancel Result (Discriminated Union)
type BatchCancelResult =
| {
index: number;
orderId: string;
success: true;
status: { success: { oid: string } };
}
| {
index: number;
orderId: string;
success: false;
status: { error: string };
};
// Batch Cancel Response
interface CancelMultipleOrdersResponse {
results: BatchCancelResult[];
successCount: number;
failureCount: number;
}
// Type Guards for Order Status
function isRestingStatus(status: OrderStatus): status is { resting: { oid: string } } {
return 'resting' in status;
}
function isFilledStatus(status: OrderStatus): status is { filled: { oid: string; totalSz: string; avgPx: string } } {
return 'filled' in status;
}
function isErrorStatus(status: OrderStatus): status is { error: string } {
return 'error' in status;
}
// Type Guards for Cancel Status
function isCancelSuccess(status: CancelStatus): status is { success: { oid: string } } {
return 'success' in status;
}
function isCancelError(status: CancelStatus): status is { error: string } {
return 'error' in status;
}🎯 Type-Safe Response Handling
The SDK provides discriminated unions for type-safe response handling. TypeScript automatically narrows types based on runtime checks.
Single Order Response
import { MiniAppSDK, isRestingStatus, isFilledStatus, isErrorStatus } from '@basedone/miniapp-sdk';
const client = new MiniAppSDK({ appId: 'my-app' });
const response = await client.placeOrder({
symbol: 'ETH',
side: 'buy',
orderType: 'limit',
price: 2000,
size: 0.1
});
if (response.success && response.data) {
const status = response.data.statuses[0];
// TypeScript knows the shape based on runtime check
if (isRestingStatus(status)) {
console.log(`Order resting in orderbook: ${status.resting.oid}`);
// TypeScript knows: status.resting.oid exists
} else if (isFilledStatus(status)) {
console.log(`Order filled: ${status.filled.totalSz} @ ${status.filled.avgPx}`);
// TypeScript knows: status.filled.totalSz, status.filled.avgPx exist
} else if (isErrorStatus(status)) {
console.error(`Order failed: ${status.error}`);
// TypeScript knows: status.error exists
}
}Batch Order Response with Type Safety
const orders = [
{ symbol: 'ETH', side: 'buy', orderType: 'market', size: 0.1 },
{ symbol: 'BTC', side: 'buy', orderType: 'limit', price: 50000, size: 0.01 },
{ symbol: 'SOL', side: 'buy', orderType: 'market', size: 5 }
];
const response = await client.placeOrders(orders);
if (response.success && response.data) {
const { successCount, failureCount, results } = response.data;
console.log(`Batch: ${successCount} succeeded, ${failureCount} failed`);
// Process each result with type safety
results.forEach((result, idx) => {
if (result.success) {
// TypeScript knows: result.status can be 'resting' or 'filled'
if ('resting' in result.status) {
console.log(`Order ${idx}: Resting (${result.status.resting.oid})`);
} else {
// TypeScript knows this is 'filled'
const { oid, totalSz, avgPx } = result.status.filled;
console.log(`Order ${idx}: Filled ${totalSz} @ ${avgPx} (${oid})`);
}
} else {
// TypeScript knows: result.status.error exists
console.error(`Order ${idx}: Failed - ${result.status.error}`);
}
});
}React Example with Type Safety
import React from 'react';
import { useMiniApp, isFilledStatus } from '@basedone/miniapp-sdk/react';
function TradingComponent() {
const { client } = useMiniApp();
const [orderStatus, setOrderStatus] = React.useState<string>('');
const handleOrder = async () => {
if (!client) return;
const response = await client.placeOrder({
symbol: 'ETH',
side: 'buy',
orderType: 'market',
size: 0.1
});
if (response.success && response.data) {
const status = response.data.statuses[0];
if (isFilledStatus(status)) {
// Full type safety - TypeScript knows all these fields exist
setOrderStatus(
`Filled ${status.filled.totalSz} @ $${status.filled.avgPx}`
);
} else if ('resting' in status) {
setOrderStatus(`Order placed: ${status.resting.oid}`);
} else {
setOrderStatus(`Error: ${status.error}`);
}
}
};
return (
<div>
<button onClick={handleOrder}>Place Order</button>
{orderStatus && <p>{orderStatus}</p>}
</div>
);
}📚 Documentation
- Introduction - Overview and concepts
- Quick Start - Get started in 5 minutes
- Installation & Setup - Development environment
- Architecture - System design and patterns
- Commands API Reference - Complete command reference
- Event Guide - Event types and handling
- System Overview - MiniApp system architecture
- Deployment Guide - Deploying your mini-app
💡 Examples
Get Wallet Address (Vanilla JavaScript)
// Retrieve the connected user's wallet address
const client = new MiniAppSDK({
appId: 'my-app',
debug: true
});
client.on('connected', async () => {
try {
const address = await client.getWalletAddress();
console.log('Connected wallet:', address);
} catch (error) {
console.error('Failed to get wallet address:', error);
}
});
// Listen for wallet switch events
client.on('wallet.switch', (event) => {
console.log('Wallet switched to:', event.address);
});Get Wallet Address (React Hook)
import React from 'react';
import { MiniAppProvider, useWalletAddress } from '@basedone/miniapp-sdk/react';
function WalletDisplay() {
const { address, loading, error, refresh } = useWalletAddress();
if (loading) return <div>Loading wallet...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h3>Connected Wallet</h3>
<p>{address || 'No wallet connected'}</p>
<button onClick={refresh}>Refresh</button>
</div>
);
}
function App() {
return (
<MiniAppProvider config={{ appId: 'my-app' }}>
<WalletDisplay />
</MiniAppProvider>
);
}Populate Order Form
// Populate the terminal's order form with pre-filled values
const client = new MiniAppSDK({
appId: 'trading-assistant',
debug: true
});
// Populate order form with calculated values
await client.sendCommand('order.populate', {
symbol: 'ETH',
side: 'buy',
orderType: 'limit',
size: 0.5,
price: 2000,
// Add TP/SL levels
tpsl: {
tpPrice: 2200, // Take profit at $2200
slPrice: 1900, // Stop loss at $1900
// Or use percentage-based TP/SL
// tpGainPercent: 10, // 10% profit
// slLossPercent: 5 // 5% loss
}
});Batch Order Placement
// Place multiple orders simultaneously for efficient portfolio management
const client = new MiniAppSDK({
appId: 'batch-trader',
debug: true,
autoConnect: true
});
client.on('connected', async () => {
// Portfolio rebalancing with batch orders
const orders = [
{
symbol: 'ETH',
side: 'buy',
orderType: 'market',
size: 0.1
},
{
symbol: 'BTC',
side: 'buy',
orderType: 'limit',
size: 0.01,
price: 50000
},
{
symbol: 'SOL',
side: 'buy',
orderType: 'market',
size: 5,
tpsl: {
tpGainPercent: 10, // Take profit at 10%
slLossPercent: 5 // Stop loss at 5%
}
}
];
try {
const result = await client.placeOrders(orders);
if (result.success && result.data) {
const { successCount, failureCount, results } = result.data;
console.log(`Batch complete: ${successCount} succeeded, ${failureCount} failed`);
// Check individual results
results.forEach((res, idx) => {
if (res.success) {
console.log(`Order ${idx}: Placed successfully (${res.cloid})`);
} else {
console.error(`Order ${idx}: Failed - ${res.error}`);
}
});
}
} catch (error) {
console.error('Batch order failed:', error);
}
});
// DCA strategy using batch orders
async function executeDCA(symbols, amountEach) {
const orders = symbols.map(symbol => ({
symbol,
side: 'buy',
orderType: 'market',
size: amountEach,
tpsl: {
tpGainPercent: 15,
slLossPercent: 5
}
}));
return await client.placeOrders(orders);
}
// Execute DCA for multiple assets
await executeDCA(['ETH', 'BTC', 'SOL', 'AVAX'], 0.1);Batch Order Cancellation
import { MiniAppSDK, isCancelSuccess, isCancelError } from '@basedone/miniapp-sdk';
const client = new MiniAppSDK({
appId: 'order-manager',
debug: true,
autoConnect: true
});
client.on('connected', async () => {
// Cancel multiple orders simultaneously
const ordersToCancel = [
{ orderId: 'order-123', symbol: 'ETH' },
{ orderId: 'order-456', symbol: 'BTC' },
{ orderId: 'order-789', symbol: 'SOL' }
];
try {
const response = await client.cancelOrders(ordersToCancel);
if (response.success && response.data) {
const { successCount, failureCount, results } = response.data;
console.log(`Batch cancel: ${successCount} succeeded, ${failureCount} failed`);
// Process each result with type safety
results.forEach((result, idx) => {
if (result.success) {
// TypeScript knows: result.status.success exists
console.log(`Cancel ${idx}: Success (${result.status.success.oid})`);
} else {
// TypeScript knows: result.status.error exists
console.error(`Cancel ${idx}: Failed - ${result.status.error}`);
}
});
}
} catch (error) {
console.error('Batch cancel failed:', error);
}
});
// Example: Cancel all open orders for a symbol
async function cancelAllOrdersForSymbol(symbol: string) {
// First, get all open orders for the symbol
const positions = await client.getPositions(symbol);
// Extract order IDs (this depends on your data structure)
const orderIds = positions.data?.positions
.flatMap(p => p.orders || [])
.map(order => ({ orderId: order.id, symbol }));
if (orderIds && orderIds.length > 0) {
return await client.cancelOrders(orderIds);
}
}
// Cancel all orders for ETH
await cancelAllOrdersForSymbol('ETH');Retrieve Open Orders
import { MiniAppSDK, Order } from '@basedone/miniapp-sdk';
const client = new MiniAppSDK({
appId: 'order-monitor',
permissions: ['read_orders'],
debug: true,
autoConnect: true
});
client.on('connected', async () => {
// Get all open orders
const response = await client.getOrders();
if (response.success && response.data) {
const { orders, totalCount } = response.data;
console.log(`Found ${totalCount} open orders`);
orders.forEach((order: Order) => {
console.log(`
Order ID: ${order.orderId}
Symbol: ${order.symbol}
Side: ${order.side}
Type: ${order.orderType}
Size: ${order.filledSize}/${order.size}
Price: ${order.price || 'Market'}
Status: ${order.status}
${order.tpsl ? `TP: ${order.tpsl.tpPrice}, SL: ${order.tpsl.slPrice}` : ''}
`);
});
}
// Get open orders for specific symbol
const ethOrders = await client.getOrders('ETH', 'open');
if (ethOrders.success && ethOrders.data) {
console.log(`ETH open orders: ${ethOrders.data.totalCount}`);
}
// Get all orders (including filled/cancelled)
const allOrders = await client.getOrders(undefined, 'all');
if (allOrders.success && allOrders.data) {
console.log(`Total orders: ${allOrders.data.totalCount}`);
// Filter by status
const filledOrders = allOrders.data.orders.filter(o => o.status === 'filled');
const cancelledOrders = allOrders.data.orders.filter(o => o.status === 'cancelled');
console.log(`Filled: ${filledOrders.length}, Cancelled: ${cancelledOrders.length}`);
}
// Server-side filtering by status (more efficient)
const filled = await client.getOrders(undefined, 'filled');
console.log(`Filled orders: ${filled.data?.totalCount || 0}`);
const cancelled = await client.getOrders(undefined, 'cancelled');
console.log(`Cancelled orders: ${cancelled.data?.totalCount || 0}`);
const partial = await client.getOrders(undefined, 'partial');
console.log(`Partially filled orders: ${partial.data?.totalCount || 0}`);
});
// Listen for order updates in real-time
client.on('order.update', (order: Order) => {
console.log(`Order ${order.orderId} updated: ${order.status}`);
if (order.status === 'filled') {
console.log(`Order filled: ${order.filledSize} ${order.symbol} @ ${order.price}`);
}
});React Filled Orders Tracker
import React from 'react';
import { MiniAppProvider, useBasedOrders } from '@basedone/miniapp-sdk/react';
import type { Order } from '@basedone/miniapp-sdk';
function App() {
return (
<MiniAppProvider config={{
appId: 'trade-history',
permissions: ['read_orders'],
debug: true
}}>
<FilledOrdersTracker />
</MiniAppProvider>
);
}
function FilledOrdersTracker() {
const { orders, getFilledOrders, loading } = useBasedOrders();
const [selectedSymbol, setSelectedSymbol] = React.useState<string>('');
React.useEffect(() => {
// Load filled orders on mount
getFilledOrders();
}, []);
const handleSymbolFilter = async (symbol: string) => {
setSelectedSymbol(symbol);
if (symbol) {
await getFilledOrders(symbol);
} else {
await getFilledOrders();
}
};
if (loading) return <div>Loading trade history...</div>;
// Calculate statistics
const totalVolume = orders.reduce((sum, order) => sum + (order.filledSize * (order.price || 0)), 0);
const buyOrders = orders.filter(o => o.side === 'buy');
const sellOrders = orders.filter(o => o.side === 'sell');
return (
<div style={{ padding: '20px' }}>
<h2>📈 Trade History</h2>
<div style={{ marginBottom: '20px' }}>
<label>Filter by symbol: </label>
<select
value={selectedSymbol}
onChange={(e) => handleSymbolFilter(e.target.value)}
>
<option value="">All Symbols</option>
{Array.from(new Set(orders.map(o => o.symbol))).map(symbol => (
<option key={symbol} value={symbol}>{symbol}</option>
))}
</select>
</div>
{/* Statistics */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px', marginBottom: '30px' }}>
<div style={{ padding: '15px', background: '#f5f5f5', borderRadius: '8px' }}>
<strong>Total Trades</strong>
<div style={{ fontSize: '24px', marginTop: '10px' }}>{orders.length}</div>
</div>
<div style={{ padding: '15px', background: '#e8f5e9', borderRadius: '8px' }}>
<strong>Buy Orders</strong>
<div style={{ fontSize: '24px', marginTop: '10px', color: 'green' }}>{buyOrders.length}</div>
</div>
<div style={{ padding: '15px', background: '#ffebee', borderRadius: '8px' }}>
<strong>Sell Orders</strong>
<div style={{ fontSize: '24px', marginTop: '10px', color: 'red' }}>{sellOrders.length}</div>
</div>
</div>
{/* Orders List */}
<h3>Filled Orders ({orders.length})</h3>
{orders.length === 0 ? (
<p>No filled orders found</p>
) : (
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '2px solid #ccc' }}>
<th>Time</th>
<th>Symbol</th>
<th>Side</th>
<th>Type</th>
<th>Size</th>
<th>Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order.orderId} style={{ borderBottom: '1px solid #eee' }}>
<td>{new Date(order.updatedAt).toLocaleString()}</td>
<td>{order.symbol}</td>
<td style={{ color: order.side === 'buy' ? 'green' : 'red' }}>
{order.side.toUpperCase()}
</td>
<td>{order.orderType}</td>
<td>{order.filledSize}</td>
<td>${order.price?.toFixed(2) || 'Market'}</td>
<td>${((order.filledSize * (order.price || 0))).toFixed(2)}</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}React Orders Manager
import React from 'react';
import { MiniAppProvider, useMiniApp, useBasedOrders } from '@basedone/miniapp-sdk/react';
import type { Order } from '@basedone/miniapp-sdk';
function App() {
return (
<MiniAppProvider config={{
appId: 'orders-manager',
permissions: ['read_orders', 'cancel_orders'],
debug: true
}}>
<OrdersManager />
</MiniAppProvider>
);
}
function OrdersManager() {
const { client, connected } = useMiniApp();
const { orders, getOpenOrders, refreshOrders, loading } = useBasedOrders();
const [selectedSymbol, setSelectedSymbol] = React.useState<string>('');
React.useEffect(() => {
if (connected) {
// Refresh orders on mount
refreshOrders();
// Set up auto-refresh every 5 seconds
const interval = setInterval(refreshOrders, 5000);
return () => clearInterval(interval);
}
}, [connected, refreshOrders]);
const handleSymbolFilter = async (symbol: string) => {
setSelectedSymbol(symbol);
if (symbol) {
await getOpenOrders(symbol);
} else {
await refreshOrders();
}
};
const handleCancelOrder = async (orderId: string, symbol: string) => {
if (!client) return;
try {
await client.cancelOrder(orderId, symbol);
await refreshOrders(); // Refresh after cancellation
} catch (error) {
console.error('Failed to cancel order:', error);
}
};
if (!connected) return <div>Connecting...</div>;
if (loading) return <div>Loading orders...</div>;
// Group orders by symbol
const ordersBySymbol = orders.reduce((acc, order) => {
if (!acc[order.symbol]) acc[order.symbol] = [];
acc[order.symbol].push(order);
return acc;
}, {} as Record<string, Order[]>);
return (
<div style={{ padding: '20px' }}>
<h2>📋 Orders Manager</h2>
<div style={{ marginBottom: '20px' }}>
<label>Filter by symbol: </label>
<select
value={selectedSymbol}
onChange={(e) => handleSymbolFilter(e.target.value)}
>
<option value="">All Symbols</option>
{Object.keys(ordersBySymbol).map(symbol => (
<option key={symbol} value={symbol}>{symbol}</option>
))}
</select>
<button onClick={() => refreshOrders()} style={{ marginLeft: '10px' }}>
Refresh
</button>
</div>
<div>
<h3>Open Orders ({orders.length})</h3>
{orders.length === 0 ? (
<p>No open orders</p>
) : (
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ borderBottom: '2px solid #ccc' }}>
<th>Symbol</th>
<th>Side</th>
<th>Type</th>
<th>Size</th>
<th>Filled</th>
<th>Price</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order.orderId} style={{ borderBottom: '1px solid #eee' }}>
<td>{order.symbol}</td>
<td style={{ color: order.side === 'buy' ? 'green' : 'red' }}>
{order.side.toUpperCase()}
</td>
<td>{order.orderType}</td>
<td>{order.size}</td>
<td>{order.filledSize}</td>
<td>{order.price || 'Market'}</td>
<td>
<span style={{
padding: '2px 8px',
borderRadius: '4px',
background: order.status === 'open' ? '#e3f2fd' : '#fff3e0'
}}>
{order.status}
</span>
</td>
<td>
<button
onClick={() => handleCancelOrder(order.orderId, order.symbol)}
style={{
padding: '4px 12px',
background: '#f44336',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Cancel
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
{/* Summary by symbol */}
<div style={{ marginTop: '30px' }}>
<h3>Summary by Symbol</h3>
{Object.entries(ordersBySymbol).map(([symbol, symbolOrders]) => (
<div key={symbol} style={{ marginBottom: '10px' }}>
<strong>{symbol}:</strong> {symbolOrders.length} orders
{' '}({symbolOrders.filter(o => o.side === 'buy').length} buy,
{symbolOrders.filter(o => o.side === 'sell').length} sell)
</div>
))}
</div>
</div>
);
}Basic Trading Bot
// Simple DCA (Dollar Cost Averaging) bot
const client = new MiniAppSDK({
appId: 'dca-bot',
debug: true,
autoConnect: true
});
client.on('connected', async () => {
// Subscribe to ETH price updates
await client.subscribeToMarkets(['ETH']);
client.on('market.prices', async (data) => {
if (data.symbol === 'ETH' && shouldBuy(data.price)) {
try {
const result = await client.placeOrder({
symbol: 'ETH',
side: 'buy',
orderType: 'market',
size: 0.01, // Buy 0.01 ETH
// Add TP/SL for risk management
tpsl: {
tpGainPercent: 5, // Take profit at 5% gain
slLossPercent: 2 // Stop loss at 2% loss
}
});
console.log(`DCA: Bought ETH at $${data.price}`);
} catch (error) {
console.error('DCA order failed:', error);
}
}
});
});
function shouldBuy(currentPrice) {
// Your DCA logic here
return Math.random() > 0.95; // 5% chance for demo
}React Batch Order Component
import React, { useState } from 'react';
import { MiniAppProvider, useMiniApp } from '@basedone/miniapp-sdk/react';
function App() {
return (
<MiniAppProvider config={{
appId: 'batch-trader',
permissions: ['place_orders'],
debug: true
}}>
<BatchOrderWidget />
</MiniAppProvider>
);
}
function BatchOrderWidget() {
const { placeOrders, loading, connected } = useMiniApp();
const [status, setStatus] = useState('');
const handleBatchOrder = async () => {
if (!placeOrders) return;
const orders = [
{ symbol: 'ETH', side: 'buy', orderType: 'market', size: 0.1 },
{ symbol: 'BTC', side: 'buy', orderType: 'market', size: 0.01 },
{ symbol: 'SOL', side: 'buy', orderType: 'market', size: 5 }
];
try {
const result = await placeOrders(orders);
if (result.success && result.data) {
setStatus(`Success: ${result.data.successCount}/${orders.length} orders placed`);
}
} catch (err) {
setStatus(`Error: ${err.message}`);
}
};
if (!connected) return <div>Connecting...</div>;
return (
<div>
<h2>Batch Order Placement</h2>
<button onClick={handleBatchOrder} disabled={loading}>
{loading ? 'Placing...' : 'Place 3 Orders'}
</button>
{status && <div>{status}</div>}
</div>
);
}React Portfolio Tracker
import React from 'react';
import { MiniAppProvider, useMiniApp, usePositions } from '@basedone/miniapp-sdk/react';
function App() {
return (
<MiniAppProvider config={{
appId: 'portfolio-tracker',
permissions: ['read_positions', 'read_balance'],
debug: true
}}>
<PortfolioTracker />
</MiniAppProvider>
);
}
function PortfolioTracker() {
const { client, connected } = useMiniApp();
const { positions, refresh, loading } = usePositions(client);
const [balance, setBalance] = React.useState<any>(null);
React.useEffect(() => {
if (connected && client) {
// Fetch initial balance
client.getAccount().then(setBalance);
// Refresh positions
refresh();
// Set up auto-refresh
const interval = setInterval(() => {
refresh();
client.getAccount().then(setBalance);
}, 5000);
return () => clearInterval(interval);
}
}, [connected, client]);
if (!connected) return <div>Connecting...</div>;
if (loading) return <div>Loading...</div>;
return (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
<div>
<h3>Positions</h3>
{positions.map(pos => (
<div key={pos.symbol}>
{pos.symbol}: {pos.size} @ ${pos.entryPrice}
<br />PnL: ${pos.pnl?.toFixed(2)}
</div>
))}
</div>
<div>
<h3>Balance</h3>
{balance && (
<div>Total: ${balance.totalUsdValue?.toFixed(2)}</div>
)}
</div>
</div>
);
}🛠️ Development
# Install dependencies
npm install
# Run development mode
npm run dev
# Build for production
npm run build
# Run tests
npm test
# Type check
npm run type-check🔗 Links
- Documentation: View comprehensive docs
- TypeScript Types: Full type definitions
- React Hooks: Hook library
- Core Client: SDK implementation
📄 License
MIT License - see LICENSE for details.
Made with ❤️ for Based.One developers
