npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@decibeltrade/sdk

v0.2.8

Published

TypeScript SDK for interacting with Decibel, a fully on-chain trading engine built on Aptos.

Readme

@decibeltrade/sdk

TypeScript SDK for interacting with Decibel, a fully on-chain trading engine built on Aptos.

📚 View Full Documentation →

Overview

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

Installation

npm install @decibeltrade/sdk @aptos-labs/ts-sdk zod
yarn add @decibeltrade/sdk @aptos-labs/ts-sdk zod
pnpm add @decibeltrade/sdk @aptos-labs/ts-sdk zod

For TypeScript in Node.js environments, you may also want:

npm install -D @types/ws

Quick Start

Read: Market and Account Data

import { 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 });

Write: Submit Transactions

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
});

Documentation

Getting Started

Read SDK

  • 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

  • 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

Configuration

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);

Custom Configuration

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://your-fee-payer.com",
  deployment: {
    package: "0x...",
    usdc: "0x...",
    testc: "0x...",
    perpEngineGlobal: "0x...",
  },
};

When to Use Which

  • Use DecibelReadDex when you need market data, order/position history, or account state. No private keys required.
  • Use DecibelWriteDex for on-chain actions and trading. In browsers, avoid embedding private keys — prefer session keys or a wallet and pass accountOverride for specific calls.

Read Operations API

DecibelReadDex

The main read client providing access to all market data and account information.

Constructor

new DecibelReadDex(config: DecibelConfig, opts?: {
  nodeApiKey?: string;
  onWsError?: (error: ErrorEvent) => void;
})

Global Methods

// 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...");

Markets

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...");

Account Overview

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();

User Positions

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),
);

User Orders

Query open orders and order history.

Open Orders

// 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),
);

Order History

// 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),
);

Market Data

Market Depth (Order Book)

// 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");

Market Prices

// 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),
);

Market Trades

// 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),
);

Candlesticks

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),
);

Market Contexts

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),
);

User Trade History

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),
);

User Funding History

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),
);

User Subaccounts

// Get all subaccounts for a user
const subaccounts = await readDex.userSubaccounts.getByAddr("account_address");

Vaults

// Get user vault positions
const userVaults = await readDex.userVaults.getByAddr("account_address");

// Get public vault information
const vaults = await readDex.vaults.getAll();

Write Operations API

DecibelWriteDex

The main write client for executing trades and managing account operations.

Constructor

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;
});

Account Management

Subaccount Operations

// 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");

Trading Delegation

// 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",
});

Market Configuration

// 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)
});

Order Management

Place Orders

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,
});

Advanced Order Types

// 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
});

TWAP Orders

// 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 Orders

// 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
});

Position Management

Take-Profit / Stop-Loss Orders

// 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
});

Session Accounts

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,
});

Error Handling

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);
}

Constants and Enums

Time in Force

export const TimeInForce = {
  GoodTillCanceled: 0,
  PostOnly: 1,
  ImmediateOrCancel: 2,
} as const;

Candlestick Intervals

export enum CandlestickInterval {
  MINUTE_1 = "1m",
  MINUTE_5 = "5m",
  MINUTE_15 = "15m",
  HOUR_1 = "1h",
  HOUR_4 = "4h",
  DAY_1 = "1d",
}

Utilities

Address Utilities

import { getPrimarySubaccountAddr, getMarketAddr } from "@decibeltrade/sdk";

// Get primary subaccount address for an account
const subaccountAddr = getPrimarySubaccountAddr("account_address", sdkConfig.subaccountVariant);

// Get market address from name
const marketAddr = getMarketAddr("BTC-USD", "perp_engine_global_address");

WebSocket Subscriptions

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
  },
});

TypeScript Types

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";

Best Practices

  1. Connection Management: Reuse SDK instances where possible to maintain WebSocket connections.

  2. Error Handling: Always wrap SDK calls in try-catch blocks and handle errors appropriately.

  3. Subscription Cleanup: Always call unsubscribe functions to prevent memory leaks.

  4. Rate Limiting: Be mindful of API rate limits when making frequent requests.

  5. Account Security: Never expose private keys in client-side code. Use environment variables or secure key management.

  6. Precision: Be careful with number precision for prices and sizes. The SDK handles decimal precision internally.

Complete Trading Bot Example

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();

Smart Contract Transactions

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.

Core Transaction Infrastructure

Base Transaction Manager

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;
  private noFeePayer: boolean;

  constructor(
    config: DecibelConfig,
    private account: Account,
    options?: {
      skipSimulate?: boolean;
      noFeePayer?: boolean;
      nodeApiKey?: string;
    },
  ) {
    this.config = config;
    this.skipSimulate = options?.skipSimulate ?? false;
    this.noFeePayer = options?.noFeePayer ?? 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),
      },
    });
  }

  private async submitTransaction(
    transaction: SimpleTransaction,
    senderAuthenticator: AccountAuthenticator,
  ): Promise<PendingTransactionResponse> {
    if (this.noFeePayer) {
      return await this.aptos.transaction.submit.simple({
        transaction,
        senderAuthenticator,
      });
    } else {
      return await this.submitFeePaidTransaction(transaction, senderAuthenticator);
    }
  }

  private async submitFeePaidTransaction(
    transaction: SimpleTransaction,
    senderAuthenticator: AccountAuthenticator,
  ): Promise<PendingTransactionResponse> {
    const signatureBcs = Array.from(senderAuthenticator.bcsToBytes());
    const transactionBcs = Array.from(transaction.rawTransaction.bcsToBytes());

    const response = await fetch(this.config.gasStationUrl + "/transactions", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        signature: signatureBcs,
        transaction: transactionBcs,
      }),
    });

    if (!response.ok) {
      throw new Error(`Fee payer service error: ${response.status}`);
    }

    return (await response.json()) as PendingTransactionResponse;
  }

  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 {
      const withFeePayer = !this.noFeePayer;
      transaction = await this.aptos.transaction.build.simple({
        sender,
        data: payload,
        withFeePayer,
      });
    }

    const senderAuthenticator = this.aptos.transaction.sign({
      signer,
      transaction,
    });

    const pendingTransaction = await this.submitTransaction(transaction, senderAuthenticator);
    return await this.aptos.waitForTransaction({
      transactionHash: pendingTransaction.hash,
    });
  }
}

Utility Functions

/**
 * 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 {
  const seed = new TextEncoder().encode("decibel_dex_primary_v2");
  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;
  }
}

Account Management Transactions

Create Subaccount

async function createSubaccount(
  transactionManager: DecibelTransactionManager,
  config: DecibelConfig,
): Promise<CommittedTransactionResponse> {
  return await transactionManager.sendTransaction({
    function: `${config.deployment.package}::dex_accounts::create_new_subaccount`,
    typeArguments: [],
    functionArguments: [],
  });
}

Deposit Collateral

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],
  });
}

Withdraw Collateral

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_subaccount`,
    typeArguments: [],
    functionArguments: [subaccount, config.deployment.usdc, amount],
  });
}

Configure Market Settings

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],
  });
}

Order Management Transactions

Place Order

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::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",
    };
  }
}

Cancel Order

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,
  );
}

Place TWAP Order

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,
  );
}

Position Management Transactions

Place TP/SL Order

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,
  );
}

Trading Delegation

Delegate Trading

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],
  });
}

Revoke Delegation

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],
  });
}

Complete Working Example

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,
    noFeePayer: 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);
  }
}

Best Practices

  1. Error Handling: Always wrap transaction calls in try-catch blocks and handle different types of errors appropriately.

  2. Gas Management: Use skipSimulate: false for gas estimation in production. Set appropriate gas limits for complex transactions.

  3. Subaccount Management: Use primary subaccount for simple use cases. Create separate subaccounts for different strategies. Always verify subaccount addresses before transactions.

  4. Market Address Handling: Cache market addresses to avoid repeated calculations. Verify market names are correct before generating addresses.

  5. Order Management: Store order IDs for later cancellation. Use client order IDs for easier tracking. Implement proper order status monitoring.

Resources