TypeScript SDK for interacting with Decibel, a fully on-chain trading engine built on Aptos.
π View Full Documentation β
The Decibel TypeScript SDK provides a clean, typed interface to interact with Decibel on Aptos:
- Read operations (
DecibelReadDex) β Query markets, depth, prices, trades, positions, orders, subaccounts, vaults - Write operations (
DecibelWriteDex) β Place/cancel orders, manage positions and subaccounts, vault operations, delegation
npm install @decibeltrade/sdk @aptos-labs/ts-sdk zodyarn add @decibeltrade/sdk @aptos-labs/ts-sdk zodpnpm add @decibeltrade/sdk @aptos-labs/ts-sdk zodFor TypeScript in Node.js environments, you may also want:
npm install -D @types/wsimport { DecibelReadDex, NETNA_CONFIG } from "@decibeltrade/sdk";
const read = new DecibelReadDex(NETNA_CONFIG, {
nodeApiKey: process.env.APTOS_NODE_API_KEY, // required
onWsError: (error) => console.error("WebSocket error:", error), // optional
});
// Get all markets
const markets = await read.markets.getAll();
// Get account overview
const account = await read.accountOverview.getByAddr("0x...account");
// Get market prices
const prices = await read.marketPrices.getAll();
// Get order book depth
const depth = await read.marketDepth.getBySymbol("BTC-PERP", { depth: 10 });import { DecibelWriteDex, NETNA_CONFIG } from "@decibeltrade/sdk";
import { Ed25519Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";
const account = new Ed25519Account({
privateKey: new Ed25519PrivateKey(process.env.PRIVATE_KEY!),
});
const write = new DecibelWriteDex(NETNA_CONFIG, account, {
nodeApiKey: process.env.APTOS_NODE_API_KEY, // optional
});
// Place an order
const order = await write.placeOrder({
subaccountAddr: "0x...",
marketAddr: "0x...",
price: 5670000000, // 5.67 with 9 decimals
size: 1000000000, // 1.0 with 9 decimals
isBuy: true,
timeInForce: 0, // GoodTillCanceled
});- SDK Overview - Introduction to the TypeScript SDK
- Installation - Install and configure the SDK
- Configuration - Network configuration and presets
- Read SDK Guide - Query market data and account information
- Market data (prices, depth, trades, candlesticks)
- Account overview and positions
- Order history and open orders
- Subaccounts and delegations
- Vault information
- Write SDK Guide - Build and submit transactions
- Place and cancel orders
- TWAP and bulk orders
- Position management (TP/SL)
- Subaccount management
- Vault operations
- Delegation
- Advanced Usage - Advanced SDK features and patterns
The SDK supports multiple network configurations:
import { NETNA_CONFIG, TESTNET_CONFIG, LOCAL_CONFIG, DOCKER_CONFIG } from "@decibeltrade/sdk";
// Netna (devnet)
const read = new DecibelReadDex(NETNA_CONFIG);
// Testnet
const read = new DecibelReadDex(TESTNET_CONFIG);
// Local development
const read = new DecibelReadDex(LOCAL_CONFIG);
// Docker environment
const read = new DecibelReadDex(DOCKER_CONFIG);import { DecibelConfig } from "@decibeltrade/sdk";
import { Network } from "@aptos-labs/ts-sdk";
const customConfig: DecibelConfig = {
network: Network.CUSTOM,
fullnodeUrl: "https://api.testnet.aptoslabs.com/v1",
tradingHttpUrl: "https://api.testnet.aptoslabs.com/decibel",
tradingWsUrl: "wss://api.testnet.aptoslabs.com/decibel/ws",
gasStationUrl: "https://api.testnet.aptoslabs.com/gs/v1", // optional: enables gas sponsorship
gasStationApiKey: "your-gas-station-api-key", // optional: enables gas sponsorship
deployment: {
package: "0x...",
usdc: "0x...",
testc: "0x...",
perpEngineGlobal: "0x...",
},
};- Use
DecibelReadDexwhen you need market data, order/position history, or account state. No private keys required. - Use
DecibelWriteDexfor on-chain actions and trading. In browsers, avoid embedding private keys β prefer session keys or a wallet and passaccountOverridefor specific calls.
The main read client providing access to all market data and account information.
new DecibelReadDex(config: DecibelConfig, opts?: {
nodeApiKey?: string;
onWsError?: (error: ErrorEvent) => void;
})// Get global perpetual engine state
await readDex.globalPerpEngineState();
// Get collateral balance decimals
await readDex.collateralBalanceDecimals();
// Get USDC decimals (cached)
await readDex.usdcDecimals();
// Get USDC balance for an address
await readDex.usdcBalance("0x123...");
// Get account balance
await readDex.accountBalance("0x123...");
// Get position size
await readDex.positionSize("0x123...", "metadata_address");
// Get crossed position
await readDex.getCrossedPosition("0x123...");Access market information and configuration.
// Get all available markets
const markets = await readDex.markets.getAll();
// Get specific market by name
const market = await readDex.markets.getByName("BTC-USD");
// Get market by symbol
const market = await readDex.markets.getBySymbol("BTC-PERP");
// List all market addresses
const addresses = await readDex.markets.listMarketAddresses();
// Get market name by address
const name = await readDex.markets.marketNameByAddress("0x123...");Get comprehensive account information including balances and positions.
// Get account overview
const overview = await readDex.accountOverview.getByAddr("subaccount_address", "30d"); // volume_window = "30d"
// Subscribe to real-time account updates
const unsubscribe = readDex.accountOverview.subscribeByAddr("subaccount_address", (data) =>
console.log("Account update:", data),
);
// Later, unsubscribe
unsubscribe();Query user positions across markets.
// Get all positions for a user
const positions = await readDex.userPositions.getByAddr({
subAddr: "subaccount_address",
includeDeleted: false,
limit: 10,
});
// Get positions for specific market
const marketPositions = await readDex.userPositions.getByAddr({
subAddr: "subaccount_address",
marketAddr: "market_address",
limit: 10,
});
// Subscribe to position updates
const unsubscribe = readDex.userPositions.subscribeByAddr("subaccount_address", (data) =>
console.log("Position update:", data),
);Query open orders and order history.
// Get open orders
const openOrders = await readDex.userOpenOrders.getByAddr("subaccount_address");
// Subscribe to open orders updates
const unsubscribe = readDex.userOpenOrders.subscribeByAddr("subaccount_address", (data) =>
console.log("Orders update:", data),
);// Get order history
const orderHistory = await readDex.userOrderHistory.getByAddr({
subAddr: "subaccount_address",
marketAddr: "market_address", // optional
limit: 50,
});
// Subscribe to order history updates
const unsubscribe = readDex.userOrderHistory.subscribeByAddr("subaccount_address", (data) =>
console.log("Order history update:", data),
);// Get market depth
const depth = await readDex.marketDepth.getByName("BTC-USD", 100); // limit = 100
const depth = await readDex.marketDepth.getBySymbol("BTC-PERP", { depth: 100 });
// Subscribe to depth updates
const unsubscribe = readDex.marketDepth.subscribeByName("BTC-USD", (data) =>
console.log("Depth update:", data),
);
// Reset subscription (clear cached data)
readDex.marketDepth.resetSubscriptionByName("BTC-USD");// Get current prices
const prices = await readDex.marketPrices.getByName("BTC-USD");
const prices = await readDex.marketPrices.getAll();
// Subscribe to price updates
const unsubscribe = readDex.marketPrices.subscribeByName("BTC-USD", (data) =>
console.log("Price update:", data),
);// Get recent trades
const trades = await readDex.marketTrades.getByName("BTC-USD", 50); // limit = 50
// Subscribe to trade updates
const unsubscribe = readDex.marketTrades.subscribeByName("BTC-USD", (data) =>
console.log("Trade update:", data),
);import { CandlestickInterval } from "@decibeltrade/sdk";
// Get historical candlestick data
const candlesticks = await readDex.candlesticks.getByName(
"BTC-USD",
CandlestickInterval.MINUTE_1,
startTimestamp,
endTimestamp,
);
// Subscribe to candlestick updates
const unsubscribe = readDex.candlesticks.subscribeByName(
"BTC-USD",
CandlestickInterval.MINUTE_1,
(data) => console.log("Candlestick update:", data),
);Get additional market metadata and context.
// Get market contexts
const contexts = await readDex.marketContexts.getAll();
// Subscribe to market context updates
const unsubscribe = readDex.marketContexts.subscribeAll((data) =>
console.log("Market contexts update:", data),
);Query historical trade data for a user.
// Get trade history
const trades = await readDex.userTradeHistory.getByAddr({
subAddr: "subaccount_address",
marketAddr: "market_address", // optional
limit: 100,
});
// Subscribe to trade history updates
const unsubscribe = readDex.userTradeHistory.subscribeByAddr("subaccount_address", (data) =>
console.log("Trade history update:", data),
);Query funding payment history.
// Get funding history
const funding = await readDex.userFundingHistory.getByAddr({
subAddr: "subaccount_address",
marketAddr: "market_address", // optional
limit: 50,
});
// Subscribe to funding history updates
const unsubscribe = readDex.userFundingHistory.subscribeByAddr("subaccount_address", (data) =>
console.log("Funding history update:", data),
);// Get all subaccounts for a user
const subaccounts = await readDex.userSubaccounts.getByAddr("account_address");// Get user vault positions
const userVaults = await readDex.userVaults.getByAddr("account_address");
// Get public vault information
const vaults = await readDex.vaults.getAll();The main write client for executing trades and managing account operations.
import { Ed25519Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";
const account = new Ed25519Account({
privateKey: new Ed25519PrivateKey("your-private-key"),
});
const writeDex = new DecibelWriteDex(config, account, opts?: {
nodeApiKey?: string;
sponsorAccount?: Ed25519Account;
maxGasAmount?: number;
gasUnitPrice?: number;
});// Create a new subaccount
await writeDex.createSubaccount();
// Deposit collateral to primary subaccount
await writeDex.deposit(1000000); // amount in smallest unit
// Deposit to specific subaccount
await writeDex.deposit(1000000, "subaccount_address");
// Withdraw from subaccount
await writeDex.withdraw(500000, "subaccount_address");// Delegate trading permissions
await writeDex.delegateTradingTo({
subaccountAddr: "your_subaccount",
accountToDelegateTo: "delegate_account_address",
});
// Revoke delegation
await writeDex.revokeDelegation({
subaccountAddr: "your_subaccount",
accountToRevoke: "delegate_account_address",
});// Configure user settings for a market
await writeDex.configureUserSettingsForMarket({
marketAddr: "market_address",
subaccountAddr: "subaccount_address",
isCross: true, // cross-margin mode
userLeverage: 1000, // 10x leverage (basis points)
});import { TimeInForce } from "@decibeltrade/sdk";
// Place a limit order
const result = await writeDex.placeOrder({
marketName: "BTC-USD",
price: 45000,
size: 1.5,
isBuy: true,
timeInForce: TimeInForce.GoodTillCanceled,
isReduceOnly: false,
clientOrderId: "12345", // optional
subaccountAddr: "subaccount_address", // optional
});
// Place a post-only order
await writeDex.placeOrder({
marketName: "ETH-USD",
price: 3000,
size: 2.0,
isBuy: false,
timeInForce: TimeInForce.PostOnly,
isReduceOnly: false,
});
// Place an IOC (Immediate or Cancel) order
await writeDex.placeOrder({
marketName: "SOL-USD",
price: 100,
size: 10,
isBuy: true,
timeInForce: TimeInForce.ImmediateOrCancel,
isReduceOnly: true,
});// Place order with stop-loss and take-profit
await writeDex.placeOrder({
marketName: "BTC-USD",
price: 45000,
size: 1.0,
isBuy: true,
timeInForce: TimeInForce.GoodTillCanceled,
isReduceOnly: false,
stopPrice: 44000, // stop-loss trigger
tpTriggerPrice: 46000, // take-profit trigger
tpLimitPrice: 45900, // take-profit limit price
slTriggerPrice: 44000, // stop-loss trigger
slLimitPrice: 44100, // stop-loss limit price
});
// Place order with builder fee
await writeDex.placeOrder({
marketName: "BTC-USD",
price: 45000,
size: 1.0,
isBuy: true,
timeInForce: TimeInForce.GoodTillCanceled,
isReduceOnly: false,
builderAddr: "builder_account_address",
builderFee: 100, // fee in basis points
});// Place a Time-Weighted Average Price order
await writeDex.placeTwapOrder({
marketName: "BTC-USD",
size: 10.0,
isBuy: true,
isReduceOnly: false,
twapFrequencySeconds: 60, // execute every 60 seconds
twapDurationSeconds: 3600, // over 1 hour period
subaccountAddr: "subaccount_address", // optional
});// Cancel order by ID and market name
await writeDex.cancelOrder({
orderId: 12345,
marketName: "BTC-USD",
subaccountAddr: "subaccount_address", // optional
});
// Cancel order by ID and market address
await writeDex.cancelOrder({
orderId: 12345,
marketAddr: "market_address",
subaccountAddr: "subaccount_address", // optional
});
// Cancel order by client order ID
await writeDex.cancelClientOrder({
clientOrderId: "54321",
marketName: "BTC-USD",
subaccountAddr: "subaccount_address", // optional
});
// Cancel TWAP order
await writeDex.cancelTwapOrder({
orderId: "twap_order_id",
subaccountAddr: "subaccount_address", // optional
});
// Cancel bulk orders
await writeDex.cancelBulkOrder({
orderIds: [12345, 12346, 12347],
marketName: "BTC-USD",
subaccountAddr: "subaccount_address", // optional
});// Place TP/SL order for existing position
await writeDex.placeTpSlOrderForPosition({
marketAddr: "market_address",
tpTriggerPrice: 46000,
tpLimitPrice: 45900,
tpSize: 0.5, // partial position size
slTriggerPrice: 44000,
slLimitPrice: 44100,
slSize: 1.0, // full position size
subaccountAddr: "subaccount_address", // optional
});
// Update existing TP/SL order
await writeDex.updateTpSlOrderForPosition({
marketAddr: "market_address",
prevOrderId: "previous_order_id",
tpTriggerPrice: 47000, // new TP trigger
tpLimitPrice: 46900,
tpSize: 0.75,
// ... other parameters
});
// Cancel TP/SL order
await writeDex.cancelTpSlOrderForPosition({
marketName: "BTC-USD",
orderId: 12345,
subaccountAddr: "subaccount_address", // optional
});You can override the default account for specific transactions using session accounts:
import { Ed25519Account } from "@aptos-labs/ts-sdk";
const sessionAccount = Ed25519Account.generate();
// Use session account for this transaction
await writeDex.placeOrder({
marketName: "BTC-USD",
price: 45000,
size: 1.0,
isBuy: true,
timeInForce: TimeInForce.GoodTillCanceled,
isReduceOnly: false,
accountOverride: sessionAccount,
});All write operations return transaction results. For order placement, you get a structured result:
interface PlaceOrderResult {
success: boolean;
orderId?: string;
transactionHash: string;
error?: string;
}
const result = await writeDex.placeOrder({
// ... order parameters
});
if (result.success) {
console.log("Order placed successfully:", result.orderId);
console.log("Transaction:", result.transactionHash);
} else {
console.error("Order failed:", result.error);
}export const TimeInForce = {
GoodTillCanceled: 0,
PostOnly: 1,
ImmediateOrCancel: 2,
} as const;export enum CandlestickInterval {
MINUTE_1 = "1m",
MINUTE_5 = "5m",
MINUTE_15 = "15m",
HOUR_1 = "1h",
HOUR_4 = "4h",
DAY_1 = "1d",
}import { getPrimarySubaccountAddr, getMarketAddr } from "@decibeltrade/sdk";
// Get primary subaccount address for an account
const subaccountAddr = getPrimarySubaccountAddr(
"account_address",
sdkConfig.compat_version,
sdkConfig.deployment.package,
);
// Get market address from name
const marketAddr = getMarketAddr("BTC-USD", "perp_engine_global_address");All read operations that support real-time updates return an unsubscribe function:
// Subscribe to multiple streams
const unsubscribeDepth = readDex.marketDepth.subscribeByName("BTC-USD", handleDepth);
const unsubscribePrices = readDex.marketPrices.subscribeByName("BTC-USD", handlePrices);
const unsubscribeOrders = readDex.userOpenOrders.subscribeByAddr("subaccount", handleOrders);
// Clean up subscriptions
function cleanup() {
unsubscribeDepth();
unsubscribePrices();
unsubscribeOrders();
}
// Handle errors
const readDex = new DecibelReadDex(NETNA_CONFIG, {
onWsError: (error) => {
console.error("WebSocket error:", error);
// Implement reconnection logic
},
});The SDK is fully typed with Zod schemas for runtime validation. Import types for better development experience:
import type {
DecibelConfig,
PerpMarket,
UserPosition,
MarketDepth,
CandlestickData,
AccountOverview,
PlaceOrderResult,
} from "@decibeltrade/sdk";-
Connection Management: Reuse SDK instances where possible to maintain WebSocket connections.
-
Error Handling: Always wrap SDK calls in try-catch blocks and handle errors appropriately.
-
Subscription Cleanup: Always call unsubscribe functions to prevent memory leaks.
-
Rate Limiting: Be mindful of API rate limits when making frequent requests.
-
Account Security: Never expose private keys in client-side code. Use environment variables or secure key management.
-
Precision: Be careful with number precision for prices and sizes. The SDK handles decimal precision internally.
import { DecibelReadDex, DecibelWriteDex, NETNA_CONFIG, TimeInForce } from "@decibeltrade/sdk";
import { Ed25519Account, Ed25519PrivateKey } from "@aptos-labs/ts-sdk";
class TradingBot {
private readDex: DecibelReadDex;
private writeDex: DecibelWriteDex;
private subaccountAddr: string;
constructor(privateKey: string, subaccountAddr: string) {
this.readDex = new DecibelReadDex(NETNA_CONFIG, {
nodeApiKey: process.env.APTOS_NODE_API_KEY,
});
const account = new Ed25519Account({
privateKey: new Ed25519PrivateKey(privateKey),
});
this.writeDex = new DecibelWriteDex(NETNA_CONFIG, account, {
nodeApiKey: process.env.APTOS_NODE_API_KEY,
});
this.subaccountAddr = subaccountAddr;
}
async start() {
// Subscribe to market data
this.readDex.marketPrices.subscribeByName("BTC-USD", this.handlePriceUpdate.bind(this));
this.readDex.marketDepth.subscribeByName("BTC-USD", this.handleDepthUpdate.bind(this));
// Subscribe to account updates
this.readDex.userPositions.subscribeByAddr(
this.subaccountAddr,
this.handlePositionUpdate.bind(this),
);
}
private async handlePriceUpdate(priceData: any) {
// Implement your trading logic here
console.log("Price update:", priceData);
}
private async handleDepthUpdate(depthData: any) {
// Analyze order book for trading opportunities
console.log("Depth update:", depthData);
}
private async handlePositionUpdate(positionData: any) {
// Monitor positions and manage risk
console.log("Position update:", positionData);
}
async placeMarketBuyOrder(size: number) {
try {
const result = await this.writeDex.placeOrder({
marketName: "BTC-USD",
price: 0, // Market order (implementation may vary)
size,
isBuy: true,
timeInForce: TimeInForce.ImmediateOrCancel,
isReduceOnly: false,
subaccountAddr: this.subaccountAddr,
});
if (result.success) {
console.log(`Market buy order placed: ${result.orderId}`);
} else {
console.error(`Order failed: ${result.error}`);
}
} catch (error) {
console.error("Error placing order:", error);
}
}
}
// Usage
const bot = new TradingBot("your-private-key", "your-subaccount-address");
bot.start();For developers who need direct access to Decibel's smart contracts, this section shows how to build and submit transactions directly using the Aptos SDK.
import {
Account,
AccountAddress,
AccountAuthenticator,
Aptos,
AptosConfig,
CommittedTransactionResponse,
InputGenerateTransactionPayloadData,
MoveString,
Network,
PendingTransactionResponse,
SimpleTransaction,
createObjectAddress,
} from "@aptos-labs/ts-sdk";
class DecibelTransactionManager {
private aptos: Aptos;
private config: DecibelConfig;
private skipSimulate: boolean;
constructor(
config: DecibelConfig,
private account: Account,
options?: {
skipSimulate?: boolean;
nodeApiKey?: string;
},
) {
this.config = config;
this.skipSimulate = options?.skipSimulate ?? false;
const aptosConfig = new AptosConfig({
network: config.network,
fullnode: config.fullnodeUrl,
clientConfig: { API_KEY: options?.nodeApiKey },
});
this.aptos = new Aptos(aptosConfig);
}
private async getSimulatedTransaction(
payload: InputGenerateTransactionPayloadData,
sender: AccountAddress,
): Promise<SimpleTransaction> {
const transaction = await this.aptos.transaction.build.simple({
sender,
data: payload,
});
const [simulationResult] = await this.aptos.transaction.simulate.simple({
transaction,
options: {
estimateMaxGasAmount: true,
estimateGasUnitPrice: true,
},
});
if (!simulationResult?.max_gas_amount || !simulationResult?.gas_unit_price) {
throw new Error("Transaction simulation failed - no gas estimates returned");
}
return await this.aptos.transaction.build.simple({
sender,
data: payload,
options: {
maxGasAmount: Number(simulationResult.max_gas_amount),
gasUnitPrice: Number(simulationResult.gas_unit_price),
},
});
}
async sendTransaction(
payload: InputGenerateTransactionPayloadData,
accountOverride?: Account,
): Promise<CommittedTransactionResponse> {
const signer = accountOverride ?? this.account;
const sender = signer.accountAddress;
let transaction: SimpleTransaction;
if (!this.skipSimulate) {
transaction = await this.getSimulatedTransaction(payload, sender);
} else {
transaction = await this.aptos.transaction.build.simple({
sender,
data: payload,
});
}
const senderAuthenticator = this.aptos.transaction.sign({
signer,
transaction,
});
const pendingTransaction = await this.aptos.transaction.submit.simple({
transaction,
senderAuthenticator,
});
return await this.aptos.waitForTransaction({
transactionHash: pendingTransaction.hash,
});
}
}/**
* Get market address from market name
*/
function getMarketAddress(marketName: string, perpEngineGlobalAddr: string): AccountAddress {
const marketNameBytes = new MoveString(marketName).bcsToBytes();
return createObjectAddress(AccountAddress.fromString(perpEngineGlobalAddr), marketNameBytes);
}
/**
* Get primary subaccount address for a user account
*/
function getPrimarySubaccountAddress(userAddress: AccountAddress): string {
// TODO update
const seed = new TextEncoder().encode("primary_account");
return createObjectAddress(userAddress, seed).toString();
}
/**
* Extract order ID from transaction events
*/
function extractOrderIdFromTransaction(
txResponse: CommittedTransactionResponse,
subaccountAddr: string,
): string | null {
try {
if ("events" in txResponse && Array.isArray(txResponse.events)) {
for (const event of txResponse.events) {
if (event.type.includes("::market_types::OrderEvent")) {
const orderEvent = event.data as any;
if (orderEvent.user === subaccountAddr) {
return orderEvent.order_id;
}
}
}
}
return null;
} catch (error) {
console.error("Error extracting order ID:", error);
return null;
}
}async function createSubaccount(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
): Promise<CommittedTransactionResponse> {
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::create_new_subaccount`,
typeArguments: [],
functionArguments: [],
});
}async function depositCollateral(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
amount: number,
subaccountAddr: string,
): Promise<CommittedTransactionResponse> {
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::deposit_to_subaccount_at`,
typeArguments: [],
functionArguments: [subaccountAddr, config.deployment.usdc, amount],
});
}async function withdrawCollateral(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
amount: number,
subaccountAddr?: string,
): Promise<CommittedTransactionResponse> {
const subaccount =
subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::withdraw_from_cross_collateral`,
typeArguments: [],
functionArguments: [subaccount, config.deployment.usdc, amount],
});
}async function configureMarketSettings(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
marketAddr: string,
subaccountAddr: string,
isCross: boolean,
userLeverage: number,
): Promise<CommittedTransactionResponse> {
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::configure_user_settings_for_market`,
typeArguments: [],
functionArguments: [subaccountAddr, marketAddr, isCross, userLeverage],
});
}interface PlaceOrderResult {
success: boolean;
orderId?: string;
transactionHash: string | null;
error?: string;
}
async function placeOrder(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
marketName: string;
price: number;
size: number;
isBuy: boolean;
timeInForce: number;
isReduceOnly: boolean;
clientOrderId?: number;
stopPrice?: number;
tpTriggerPrice?: number;
tpLimitPrice?: number;
slTriggerPrice?: number;
slLimitPrice?: number;
builderAddr?: string;
builderFee?: number;
subaccountAddr?: string;
accountOverride?: Account;
},
): Promise<PlaceOrderResult> {
try {
const marketAddr = getMarketAddress(params.marketName, config.deployment.perpEngineGlobal);
const subaccountAddr =
params.subaccountAddr ??
getPrimarySubaccountAddress(transactionManager.account.accountAddress);
const txResponse = await transactionManager.sendTransaction(
{
function: `${config.deployment.package}::dex_accounts_entry::place_order_to_subaccount`,
typeArguments: [],
functionArguments: [
subaccountAddr,
marketAddr.toString(),
params.price,
params.size,
params.isBuy,
params.timeInForce,
params.isReduceOnly,
params.clientOrderId,
params.stopPrice,
params.tpTriggerPrice,
params.tpLimitPrice,
params.slTriggerPrice,
params.slLimitPrice,
params.builderAddr,
params.builderFee,
],
},
params.accountOverride,
);
const orderId = extractOrderIdFromTransaction(txResponse, subaccountAddr);
return {
success: true,
orderId: orderId || undefined,
transactionHash: txResponse.hash,
};
} catch (error) {
return {
success: false,
transactionHash: null,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}async function cancelOrder(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
orderId: number;
marketName?: string;
marketAddr?: string;
subaccountAddr?: string;
accountOverride?: Account;
},
): Promise<CommittedTransactionResponse> {
const marketAddr =
params.marketAddr ??
(params.marketName
? getMarketAddress(params.marketName, config.deployment.perpEngineGlobal).toString()
: "");
if (!marketAddr) {
throw new Error("Either marketName or marketAddr must be provided");
}
const subaccountAddr =
params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction(
{
function: `${config.deployment.package}::dex_accounts::cancel_order_to_subaccount`,
typeArguments: [],
functionArguments: [subaccountAddr, params.orderId, marketAddr],
},
params.accountOverride,
);
}async function placeTwapOrder(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
marketName: string;
size: number;
isBuy: boolean;
isReduceOnly: boolean;
twapFrequencySeconds: number;
twapDurationSeconds: number;
subaccountAddr?: string;
accountOverride?: Account;
},
): Promise<CommittedTransactionResponse> {
const marketAddr = getMarketAddress(params.marketName, config.deployment.perpEngineGlobal);
const subaccountAddr =
params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction(
{
function: `${config.deployment.package}::dex_accounts::place_twap_order_to_subaccount`,
typeArguments: [],
functionArguments: [
subaccountAddr,
marketAddr.toString(),
params.size,
params.isBuy,
params.isReduceOnly,
params.twapFrequencySeconds,
params.twapDurationSeconds,
],
},
params.accountOverride,
);
}async function placeTpSlOrderForPosition(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
marketAddr: string;
tpTriggerPrice?: number;
tpLimitPrice?: number;
tpSize?: number;
slTriggerPrice?: number;
slLimitPrice?: number;
slSize?: number;
subaccountAddr?: string;
accountOverride?: Account;
},
): Promise<CommittedTransactionResponse> {
const subaccountAddr =
params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction(
{
function: `${config.deployment.package}::dex_accounts::place_tp_sl_order_for_position`,
typeArguments: [],
functionArguments: [
subaccountAddr,
params.marketAddr,
params.tpTriggerPrice,
params.tpLimitPrice,
params.tpSize,
params.slTriggerPrice,
params.slLimitPrice,
params.slSize,
],
},
params.accountOverride,
);
}async function delegateTradingTo(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
accountToDelegateTo: string;
subaccountAddr?: string;
},
): Promise<CommittedTransactionResponse> {
const subaccountAddr =
params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::delegate_trading_to_for_subaccount`,
typeArguments: [],
functionArguments: [subaccountAddr, params.accountToDelegateTo],
});
}async function revokeDelegation(
transactionManager: DecibelTransactionManager,
config: DecibelConfig,
params: {
accountToRevoke: string;
subaccountAddr?: string;
},
): Promise<CommittedTransactionResponse> {
const subaccountAddr =
params.subaccountAddr ?? getPrimarySubaccountAddress(transactionManager.account.accountAddress);
return await transactionManager.sendTransaction({
function: `${config.deployment.package}::dex_accounts::revoke_delegation`,
typeArguments: [],
functionArguments: [subaccountAddr, params.accountToRevoke],
});
}import { Account } from "@aptos-labs/ts-sdk";
async function basicTradingExample() {
const privateKey = "your-private-key-here";
const account = Account.fromPrivateKey({ privateKey });
const transactionManager = new DecibelTransactionManager(NETNA_CONFIG, account, {
skipSimulate: false,
});
try {
// Create a subaccount
console.log("Creating subaccount...");
const createTx = await createSubaccount(transactionManager, NETNA_CONFIG);
console.log("Subaccount created:", createTx.hash);
// Get the primary subaccount address
const subaccountAddr = getPrimarySubaccountAddress(account.accountAddress);
console.log("Primary subaccount address:", subaccountAddr);
// Deposit collateral (1000 USDC = 1000000000 if 6 decimals)
console.log("Depositing collateral...");
const depositTx = await depositCollateral(
transactionManager,
NETNA_CONFIG,
1000000000,
subaccountAddr,
);
console.log("Deposit successful:", depositTx.hash);
// Configure market settings for BTC-USD
const btcMarketAddr = getMarketAddress("BTC-USD", NETNA_CONFIG.deployment.perpEngineGlobal);
console.log("Configuring market settings...");
const configTx = await configureMarketSettings(
transactionManager,
NETNA_CONFIG,
btcMarketAddr.toString(),
subaccountAddr,
true, // Use cross-margin
1000, // 10x leverage (1000 basis points)
);
console.log("Market configured:", configTx.hash);
// Place a limit buy order for 0.1 BTC at $45,000
console.log("Placing buy order...");
const orderResult = await placeOrder(transactionManager, NETNA_CONFIG, {
marketName: "BTC-USD",
price: 45000,
size: 0.1,
isBuy: true,
timeInForce: TimeInForce.GoodTillCanceled,
isReduceOnly: false,
subaccountAddr,
});
if (orderResult.success) {
console.log("Order placed successfully!");
console.log("Order ID:", orderResult.orderId);
console.log("Transaction:", orderResult.transactionHash);
// Cancel the order
if (orderResult.orderId) {
console.log("Canceling order...");
const cancelTx = await cancelOrder(transactionManager, NETNA_CONFIG, {
orderId: parseInt(orderResult.orderId),
marketName: "BTC-USD",
subaccountAddr,
});
console.log("Order canceled:", cancelTx.hash);
}
} else {
console.error("Order failed:", orderResult.error);
}
} catch (error) {
console.error("Error in trading example:", error);
}
}-
Error Handling: Always wrap transaction calls in try-catch blocks and handle different types of errors appropriately.
-
Gas Management: Use
skipSimulate: falsefor gas estimation in production. Set appropriate gas limits for complex transactions. -
Subaccount Management: Use primary subaccount for simple use cases. Create separate subaccounts for different strategies. Always verify subaccount addresses before transactions.
-
Market Address Handling: Cache market addresses to avoid repeated calculations. Verify market names are correct before generating addresses.
-
Order Management: Store order IDs for later cancellation. Use client order IDs for easier tracking. Implement proper order status monitoring.
- π Full Documentation - Complete API and transaction documentation
- π REST API - REST API reference
- π WebSocket API - WebSocket API reference
- π¬ Discord - Join our community for support
- π Trading Platform - Access the Decibel trading platform