1.1.0 • Published 2 years ago
@pepperdex/dexterity-ts v1.1.0
This is a fork of @hxronetwork/dexterity-ts updated and maintained by PepperDEX Contributors
Getting started
- Create a typescript project:
mkdir dexterity-project
cd dexterity-project
npm i typescript --save-dev
npx tsc --init- Install Dependencies:
npm install @solana/web3.js @pepperdex/dexterity-ts @coral-xyz/anchor- Create a Solana Wallet and export your private/secret-key:
- Send yourself some devnet SOL:
- Send yourself some devnet UXDC:
Index
- Create TRG, Get TRGs, Deposit, Withdraw, Place Order
- Get all MPGs and their Products for each
- Fetch, parse & stream Orderbook
- Mark & Index Price
- Account info
- Instructions Bundle
1. Create TRG, Get TRGs, Deposit, Withdraw, Place Order
import { Keypair, PublicKey } from '@solana/web3.js';
import dexterityTs from '@pepperdex/dexterity-ts';
import { Wallet } from '@coral-xyz/anchor';
const dexterity = dexterityTs;
// Configure Solana Devnet connection and relevant constants
const rpc = "your-devnet-rpc-url";
const keypair = Keypair.fromSecretKey(new Uint8Array([])); // Replace with your private key
const wallet = new Wallet(keypair);
const pubkey = 'wallet-pubkey'; // Replace with your wallet public key
const MPG = "BRWNCEzQTm8kvEXHsVVY9jpb1VLbpv9B8mkF43nMLCtu"; // Replace with the desired MPG public key
const TRG = new PublicKey("your-created-trg"); // Replace with the TRG public key
const PRODUCT_NAME = 'SOLUSD-PERP'; // Replace with the desired product name
/**
 * Creates a Trader Account (TRG) for a specified MPG.
 * Consumes ~0.16 SOL in rent, which is refundable upon account closure.
 */
const createTrg = async () => {
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    const trg = await manifest.createTrg(new PublicKey(MPG));
    console.log('\nTRG: ', trg.toBase58());
};
/**
 * Retrieves all Trader Accounts (TRGs) associated with a specified MPG for a given wallet.
 */
const getTrgs = async () => {
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    const trgArr = await manifest.getTRGsOfWallet(new PublicKey(MPG));
    const TRGs = trgArr.map(trg => trg.pubkey);
    console.log(`\nTRGs:\n`, TRGs);
};
getTrgs();
/**
 * Deposits or withdraws a specified amount to/from a Trader Account (TRG).
 */
const depositOrWithdraw = async (amount: number, type: 'deposit' | 'withdraw') => {
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    const trader = new dexterity.Trader(manifest, TRG);
    const n = dexterity.Fractional.New(amount, 0);
    await trader.connect(NaN, async () => {
        console.log(`\nBALANCE: ${Number(trader.getCashBalance()).toLocaleString()} UXDC`);
    });
    const callbacks = { 
        onGettingBlockHashFn: () => {}, 
        onGotBlockHashFn: () => {}, 
        onTxSentFn: (sig: string) => console.log(`\nSUCCESSFUL ${type.toUpperCase()} OF ${amount.toLocaleString()} UXDC\n${sig ? `SIGNATURE: https://solscan.io/tx/${sig}?cluster=devnet` : ''}`),
    };
    if (type === 'deposit') await trader.deposit(n, callbacks);
    if (type === 'withdraw') await trader.withdraw(n);
    await trader.connect(NaN, async () => {
        console.log(`\nBALANCE: ${Number(trader.getCashBalance()).toLocaleString()} USDC`);
    });
};
depositOrWithdraw(10000, 'deposit');
depositOrWithdraw(5000, 'withdraw');
/**
 * Places a buy or sell order for a specified product in the MPG.
 */
const placeOrder = async (type: 'buy' | 'sell', price: number, size: number) => {
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    const trader = new dexterity.Trader(manifest, TRG);
    await trader.connect(NaN, async () => {
        console.log(`\nBALANCE: ${trader.getCashBalance()} | OPEN ORDERS: ${(await Promise.all(trader.getOpenOrders([PRODUCT_NAME]))).length} | POSITIONS VALUE: ${trader.getPositionValue()} | PNL: ${trader.getPnL()}`);
    });
    // Find the index of the desired product within the MPG
    let ProductIndex;
    for (const [name, { index }] of trader.getProducts()) {
        if (name.trim() === PRODUCT_NAME.trim()) {
            ProductIndex = index;
            break;
        }
    }
    // Prepare order details
    const QUOTE_SIZE = dexterity.Fractional.New(size, 0);
    const priceFractional = dexterity.Fractional.New(price, 0);
    // Define callback functions for order handling
    const callbacks = {
        onGettingBlockHashFn: () => {},
        onGotBlockHashFn: () => {},
        onTxSentFn: (sig: string) => console.log(`\nSUCCESSFULLY PLACED LIMIT ${type.toUpperCase()} ORDER\nSIGNATURE: https://solscan.io/tx/${sig}?cluster=devnet`)
    };
    // Make sure Mark Prices are always up-to-date for orders to go through
    await trader.updateMarkPrices()
    // Place a limit order based on the specified type (buy or sell)
    if (type === 'buy') {
        await trader.newOrder(ProductIndex, true, priceFractional, QUOTE_SIZE, false {/*true = fillOrKill*/}, null, null, null, null, callbacks); // For a buy order
    } else if (type === 'sell') {
        await trader.newOrder(ProductIndex, false, priceFractional, QUOTE_SIZE, false {/*true = fillOrKill*/}, null, null, null, null, callbacks); // For a sell order
    }
};
placeOrder('buy', 30000, 100);
placeOrder('sell', 30000, 50);- Manifest Objectclass Manifest { fields: ManifestFields; base_api_url: string; slot: number; timestamp: Date; constructor(fields: ManifestFields); setWallet(wallet: any): void; static GetRiskAndFeeSigner(mpg: PublicKey): PublicKey; static GetStakePool(): PublicKey; getRiskS(marketProductGroup: PublicKey, mpg: any): PublicKey; getRiskR(marketProductGroup: PublicKey, mpg: any): PublicKey; static GetATAFromMPGObject(mpg: MarketProductGroup, wallet: PublicKey): Promise<web3.PublicKey>; accountSubscribe(pk: any, parseDataFn: any, onUpdateFn: any, useCache?: boolean): any; static GetMarkPrice(markPrices: MarkPricesArray, productKey: PublicKey): Fractional; static GetMarkPriceOracleMinusBookEwma(markPrices: MarkPricesArray, productKey: PublicKey): Fractional; static GetIndexPrice(markPrices: MarkPricesArray, productKey: PublicKey): Fractional; static FromFastInt(bn: BN): Fractional; getMPGFromData(data: any): Promise<MarketProductGroup>; static GetMPGFromData(dexProgram: Program, data: any): Promise<MarketProductGroup>; getMPG(mpg: PublicKey): Promise<MarketProductGroup>; static GetProductsOfMPG(mpg: MarketProductGroup): Map<any, any>; static GetActiveProductsOfMPG(mpg: MarketProductGroup): Map<any, any>; getDerivativeMetadataFromData(data: any): Promise<DerivativeMetadata>; static GetDerivativeMetadataFromData(instrumentsProgram: Program, data: any): Promise<DerivativeMetadata>; getDerivativeMetadata(productKey: PublicKey): Promise<DerivativeMetadata>; getTRGFromData(data: any): Promise<TraderRiskGroup>; getTRG(trg: PublicKey): Promise<TraderRiskGroup>; getMarkPricesAccount(marketProductGroup: PublicKey, mpg: any): PublicKey; ugh(str: any): web3.PublicKey; getMarkPricesFromData(data: any): Promise<MarkPricesArray>; getMarkPrices(markPricesAccount: PublicKey): Promise<MarkPricesArray>; getVarianceCache(varianceCache: PublicKey): Promise<VarianceCache>; getCovarianceMetadata(marketProductGroup: PublicKey, mpg: any): Promise<CovarianceMetadata>; getBook(product: any, marketState: any): Promise<{ bids: any[]; asks: any[]; }>; static aaobOrderToDexPrice(aaobOrder: LeafNode, tickSize: Fractional, offset: Fractional): Fractional; static orderIdToDexPrice(id: BN, tickSize: Fractional, offset: Fractional): Fractional; static orderIdIsBid(id: BN): boolean; streamBooks(product: any, marketState: any, onBookFn: any, onMarkPricesFn?: any): { asksSocket: ReliableWebSocket; bidsSocket: ReliableWebSocket; markPricesSocket: ReliableWebSocket; }; streamTrades(product: any, marketState: any, onTradesFn: any): ReliableWebSocket; streamMPG(mpg: PublicKey, onUpdateFn: any): ReliableWebSocket; getTRGsOfOwner(owner: PublicKey, marketProductGroup?: PublicKey): Promise<any[]>; getTRGsOfWallet(marketProductGroup?: PublicKey): Promise<any[]>; closeTrg(marketProductGroup: PublicKey, traderRiskGroup: PublicKey): Promise<any>; createTrg(marketProductGroup: PublicKey): Promise<web3.PublicKey>; fetchOrderbooks(marketProductGroup?: PublicKey): Promise<void>; fetchOrderbook(orderbook: PublicKey): Promise<any>; getFills(productName: string, trg: PublicKey, before: number, after: number): Promise<string | GetFillsResponse>; updateOrderbooks(marketProductGroup: PublicKey): Promise<void>; updateCovarianceMetadatas(): Promise<void>; static GetRiskNumber(data: any, offset: any, size: any, isSigned?: boolean): any; getStds(marketProductGroup: PublicKey): Map<any, any>; }
- Trader Objectclass Trader { manifest: Manifest; feeAccount: PublicKey; feeAccountBump: number; marketProductGroup: PublicKey; traderRiskGroup: PublicKey; riskStateAccount: PublicKey; markPricesAccount: PublicKey; hardcodedOracle: PublicKey; priceOracles: Map<string, PublicKey>; mpg: MarketProductGroup; trg: TraderRiskGroup; varianceCache: VarianceCache; markPrices: MarkPricesArray; addressLookupTableAccount: AddressLookupTableAccount; trgSocket: ReliableWebSocket; mpgSocket: ReliableWebSocket; riskSocket: ReliableWebSocket; markPricesSocket: ReliableWebSocket; trgDate: Date; mpgDate: Date; riskDate: Date; markPricesDate: Date; trgSlot: Slot; mpgSlot: Slot; riskSlot: Slot; markPricesSlot: Slot; isPaused: boolean; skipThingsThatRequireWalletConnection: boolean; constructor(manifest: Manifest, traderRiskGroup: PublicKey, skipThingsThatRequireWalletConnection?: boolean); timeTravelToDate(toDate: Date): Promise<void>; getProducts(): Map<any, any>; getPositions(): Map<any, any>; getNewOrderIx(productIndex: any, isBid: any, limitPrice: Fractional, maxBaseQty: Fractional, isIOC?: boolean, referrerTrg?: any, referrerFeeBps?: any, clientOrderId?: any, matchLimit?: any): web3.TransactionInstruction; newOrder(productIndex: any, isBid: any, limitPrice: Fractional, maxBaseQty: Fractional, isIOC: boolean, referrerTrg: any, referrerFeeBps: any, clientOrderId: any, matchLimit: any, callbacks: any): Promise<string>; justNewOrder(productIndex: any, isBid: any, limitPrice: Fractional, maxBaseQty: Fractional, isIOC: boolean, referrerTrg: any, referrerFeeBps: any, clientOrderId: any, matchLimit: any, callbacks: any): Promise<string>; updateVarianceCache(callbacks?: any): Promise<string>; getUpdateVarianceCacheIx(): web3.TransactionInstruction; getUpdateMarkPricesIx(products: Array<Product>): web3.TransactionInstruction; initializePrintTrade(isBid: any, size: any, price: any, counterparty: PublicKey): Promise<void>; getOpenOrders(productNames: any): Set<Order>; getOpenOrderIds(productName: any): Set<string>; getCancelOrderIx(productIndex: any, orderId: any, noErr?: boolean, clientOrderId?: any): web3.TransactionInstruction; cancelOrder(productIndex: any, orderId: any, noErr?: boolean, clientOrderId?: any, callbacks?: any): Promise<string>; cancelOrders(productIndex: any, orderIds: any, noErr?: boolean, clientOrderIds?: any, callbacks?: any, maxCancelsPerTx?: number): Promise<any[]>; justCancelOrder(productIndex: any, orderId: any, noErr?: boolean, clientOrderId?: any, callbacks?: any): Promise<string>; justCancelOrders(productIndex: any, orderIds: any, noErr?: boolean, clientOrderIds?: any, callbacks?: any, maxCancelsPerTx?: number): Promise<any[]>; cancelAllOrders(productNames: any, isUseCache?: boolean, justIssueCancels?: boolean, maxCancelsPerTx?: number): Promise<any[]>; getDepositIx(usdcAmount: Fractional): Promise<web3.TransactionInstruction>; fetchAddressLookupTableAccount(): Promise<void>; sendV0Tx(ixs: any, { onGettingBlockHashFn, onGotBlockHashFn, onTxSentFn }?: { onGettingBlockHashFn: any; onGotBlockHashFn: any; onTxSentFn: any; }): Promise<string>; sendLegacyTx(ixs: any, { onGettingBlockHashFn, onGotBlockHashFn, onTxSentFn }?: { onGettingBlockHashFn: any; onGotBlockHashFn: any; onTxSentFn: any; }): Promise<string>; sendTx(ixs: any, callbacks: any): Promise<string>; deposit(usdcAmount: Fractional, callbacks: any): Promise<string>; justDeposit(usdcAmount: Fractional, callbacks: any): Promise<string>; updateTraderRiskGroupOwner(newOwner: PublicKey, oldOwner?: PublicKey): Promise<void>; withdraw(usdcAmount: Fractional): Promise<void>; updateOrderbooks(): Promise<void>; disconnect(): void; streamUpdates(onUpdateFn: any): void; updateRisk(): Promise<void>; getRiskNumber(offset: any, size: any, isSigned?: boolean): any; getVarianceCacheUpdateSlot(): any; getPositionValue(): Fractional; getTradedVariance(): Fractional; getOpenOrderVariance(): Fractional; getNotionalMakerVolume(): Fractional; getNotionalTakerVolume(): Fractional; getReferredTakersNotionalVolume(): Fractional; getReferralFees(): Fractional; getCashBalance(): Fractional; getPendingCashBalance(): Fractional; getNetCash(): Fractional; getPortfolioValue(): Fractional; getTotalDeposited(): Fractional; getTotalWithdrawn(): Fractional; getDepositedCollateral(): Fractional; getPnL(): Fractional; getRequiredMaintenanceMargin(): any; getRequiredMaintenanceMarginWithoutOpenOrders(): any; getRequiredInitialMargin(): any; getRequiredInitialMarginWithoutOpenOrders(): any; getExcessMaintenanceMargin(): Fractional; getExcessInitialMargin(): Fractional; getExcessMaintenanceMarginWithoutOpenOrders(): Fractional; getExcessInitialMarginWithoutOpenOrders(): Fractional; updateMarkPrices(): Promise<void>; update(isUpdateMPG?: boolean): Promise<void>; connect(streamUpdatesCallback: any, initialUpdateCallback: any): Promise<void>; }
2. Get all MPGs and Products for each
Get all the MPGs for dexterity on the selected solana network (mainnet, devenet or testnet) and each of their products:Code Implementation:
import { Keypair } from '@solana/web3.js';
import dexterityTs from '@pepperdex/dexterity-ts';
import { Wallet } from '@coral-xyz/anchor';
const dexterity = dexterityTs;
// Replace with your private key
const keypair = Keypair.fromSecretKey(new Uint8Array([]));
// Initialize a wallet instance for Dexterity using the keypair
const wallet = new Wallet(keypair);
// Specify your RPC URL here
const rpc = 'rpc-url';
/**
 * Retrieves and logs information about all Market Product Groups (MPGs) and their associated products.
 * Filters out a specific MPG if necessary.
 */
const getMpgs = async () => {
    // Fetch the manifest from Dexterity which contains market information
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    // Convert the MPG Map to an array for easier processing
    const mpgs = Array.from(manifest.fields.mpgs.values());
    // Iterate through each MPG to access its products
    for (const { pubkey, mpg, orderbooks } of mpgs) {
        // Skip a specific MPG if needed (replace "MPG-PUBKEY-HERE" with the actual public key to skip)
        if (pubkey.toBase58() === "MPG-PUBKEY-HERE") continue;
        // Iterate through each product in the MPG
        for (const [_, { index, product }] of dexterity.Manifest.GetProductsOfMPG(mpg)) {
            // Convert the product data to a more readable format
            const meta = dexterity.productToMeta(product);
            // Log the index and name of each product for debugging and information purposes
            console.log('productIndex: ', index);
            console.log('Name: ', dexterity.bytesToString(meta.name).trim());
        }
    }
};
getMpgs();3. Orderbook
Get Live Orderbook ASK & BID data for a given product in a given MPG
Code Implementation:
import { Keypair } from '@solana/web3.js';
import dexterityTs from '@pepperdex/dexterity-ts';
import { Wallet } from '@coral-xyz/anchor';
const dexterity = dexterityTs;
// Initialize your keypair from your private/secret key
const keypair = Keypair.fromSecretKey(new Uint8Array([]));
// When working with a UI you need to use the DexterityWallet type
const wallet = new Wallet(keypair);
const rpc =
    'https://devnet.helius-rpc.com/?api-key=';
// Testnet MPG
const mpgPubkey = 'BRWNCEzQTm8kvEXHsVVY9jpb1VLbpv9B8mkF43nMLCtu';
// Desired Product
const productName = 'SOLUSD-PERP';
/**
 * Asynchronously retrieves the order book for a specific product.
 * 
 * - Fetches the manifest containing market information.
 * - Filters to find the desired Market Product Group (MPG).
 * - Iterates through products to find the specific product and its associated market state.
 * - Initializes streaming of order book data for the selected product.
 */
const getOB = async () => {
    // Fetch the manifest containing market information
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    // Convert MPG map to an array for easier processing
    const mpgs = Array.from(manifest.fields.mpgs.values());
    // Find the MPG with the matching public key
    const selectedMPG = mpgs.filter(
        (value) => value.pubkey.toBase58() === mpgPubkey,
    );
    // Initialize variables to store market state and product details
    let MarketState;
    let PRODUCT;
    let productIndex;
    // Loop through each MPG to find the desired product
    for (const { pubkey, mpg, orderbooks } of selectedMPG) {
        for (const [_, { index, product }] of dexterity.Manifest.GetProductsOfMPG(mpg)) {
            // Extract metadata of the product
            const meta = dexterity.productToMeta(product);
            // Check for the specified product name
            if (dexterity.bytesToString(meta.name).trim() === productName.trim()) {
                // Fetch the current state of the order book for the product
                MarketState = await manifest.fetchOrderbook(meta.orderbook);
                productIndex = index;
                // Determine the type of product and assign it to PRODUCT
                if (product.hasOwnProperty('outright')) {
                    PRODUCT = product.outright.outright;
                } else {
                    PRODUCT = product.combo.combo;
                }
                // Logging for debugging purposes
                console.log('productIndex: ', productIndex);
                console.log('Name: ', dexterity.bytesToString(meta.name).trim());
                // Break out of the loop once the desired product is found
                break;
            }
        }
    }
    // Stream order book data for the selected product
    const { asksSocket, bidsSocket, markPricesSocket } = manifest.streamBooks(
        PRODUCT,
        MarketState,
        aggregateBookL2 // Callback function for handling streamed data
    );
};
getOB();
/**
 * Aggregates and processes Level 2 order book data.
 * @param data The incoming order book data, containing asks and bids.
 */
function aggregateBookL2(data: any) {
    
    // Define the structure for cumulative order data
    interface CumulativeOrder {
        price: number;
        ordersSize: number;
    }
    // Check for the presence of both asks and bids
    if (data.asks.length !== 0 && data.bids.length !== 0) {
        const cumulativeBids = new Map<number, CumulativeOrder>();
        const cumulativeAsks = new Map<number, CumulativeOrder>();
        // Process bids
        data.bids.forEach((order: any) => {
            const price = order.price.toNumber();
            const quantity = order.quantity.toDecimal();
            if (cumulativeBids.has(price)) {
                cumulativeBids.get(price)!.ordersSize += quantity;
            } else {
                cumulativeBids.set(price, { price, ordersSize: quantity });
            }
        });
        // Process asks
        data.asks.forEach((offer: any) => {
            const price = offer.price.toNumber();
            const quantity = offer.quantity.toDecimal();
            if (cumulativeAsks.has(price)) {
                cumulativeAsks.get(price)!.ordersSize += quantity;
            } else {
                cumulativeAsks.set(price, { price, ordersSize: quantity });
            }
        });
        // Convert Maps to sorted arrays and limit to top 10
        const bids = Array.from(cumulativeBids.values()).sort((a, b) => b.price - a.price).slice(0, 10);
        const asks = Array.from(cumulativeAsks.values()).sort((a, b) => a.price - b.price).slice(0, 10);
        // Prepare and output the result
        let output = `Order book update received:\n\nASKS:\n`;
        asks.forEach((ask, index) => {
            output += `#${index + 1} QTY: ${ask.ordersSize} | PRICE: $${ask.price}\n`;
        });
        output += '\nBIDS:\n';
        bids.forEach((bid, index) => {
            output += `#${index + 1} QTY: ${bid.ordersSize} | PRICE: $${bid.price}\n`;
        });
        process.stdout.write(output);
        process.stdout.write('\u001b[?25h'); // Show cursor
    } else {
        // Handle missing data cases
        console.log(`Missing ${data.bids.length === 0 && data.asks.length > 0 ? 'BIDS' : data.asks.length === 0 && data.bids.length > 0 ? 'ASKS' : 'BIDS & ASKS'}`);
    }
}Example Output:
productIndex:  0
Name:  SOLUSD-PERP
Missing ASKS
Order book update received (#2):
ASKS:
#1 QTY: 0 | PRICE: $23
#2 QTY: 0 | PRICE: $23
#3 QTY: 0 | PRICE: $23
#4 QTY: 0 | PRICE: $27
#5 QTY: 0 | PRICE: $25
#6 QTY: 0 | PRICE: $23
#7 QTY: 0 | PRICE: $23
#8 QTY: 0 | PRICE: $23
#9 QTY: 0 | PRICE: $23
#10 QTY: 0 | PRICE: $23
BIDS:
#1 QTY: 0 | PRICE: $23
#2 QTY: 0 | PRICE: $23
#3 QTY: 0 | PRICE: $23
#4 QTY: 0 | PRICE: $23
#5 QTY: 0 | PRICE: $23
#6 QTY: 0 | PRICE: $23
#7 QTY: 0 | PRICE: $23
#8 QTY: 0 | PRICE: $23
#9 QTY: 0 | PRICE: $23
#10 QTY: 0 | PRICE: $234. Mark & Index Price
Code implementation:
import { Keypair, PublicKey } from '@solana/web3.js';
import dexterityTs from '@pepperdex/dexterity-ts';
import { Wallet } from '@coral-xyz/anchor';
const dexterity = dexterityTs;
// Initialize your keypair from your private/secret key
const keypair = Keypair.fromSecretKey(new Uint8Array([]));
// Initialize a wallet instance for Dexterity using the keypair
const wallet = new Wallet(keypair);
const rpc = 'https://devnet.helius-rpc.com/?api-key=';
// Testnet Market Product Group (MPG) public key
const mpgPubkey = 'BRWNCEzQTm8kvEXHsVVY9jpb1VLbpv9B8mkF43nMLCtu';
// Trader Risk Group (TRG) for the selected MPG
const TRG = new PublicKey("your-created-trg")
// Name of the desired product
const productName = 'SOLUSD-PERP';
// Uninitialized account public key
const UNINITIALIZED = new PublicKey('11111111111111111111111111111111');
/**
 * Periodically updates the market prices for products in a specified MPG.
 * - Initializes the Dexterity manifest.
 * - Creates a trader instance and repeatedly updates market prices.
 */
const updatePrices = async () => {
    // Initialize the Dexterity manifest with the RPC endpoint and wallet
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    // Create a trader instance for the specified MPG
    const trader = new dexterity.Trader(manifest, TRG);
    // Set an interval to periodically update market prices
    setInterval(async () => {
        // Update the mark prices for all products in the MPG
        await trader.updateMarkPrices();
        // Iterate through all products in the MPG
        for (const [productName, obj] of dexterity.Manifest.GetProductsOfMPG(trader.mpg)) {
            const { product } = obj;
            // Skip if the product name is empty
            if (!productName.trim()) {
                continue;
            }
            // Get metadata of the product
            const meta = dexterity.productToMeta(product);
            // Skip if the product key is uninitialized
            if (meta.productKey.equals(UNINITIALIZED)) {
                continue;
            }
            // Skip if the product is of type 'combo'
            if (product.combo?.combo) {
                continue;
            }
            // Update mark prices again (consider if this is necessary)
            await trader.updateMarkPrices();
            // Fetch and log the index and mark prices for the product
            const index = Number(dexterity.Manifest.GetIndexPrice(trader.markPrices, meta.productKey));
            const mark = Number(dexterity.Manifest.GetMarkPrice(trader.markPrices, meta.productKey));
            console.log({ index, mark });
        }
    }, 500); // The interval is set to 500 milliseconds
};5. Account info
Code implementation:
import { clusterApiUrl, Keypair, PublicKey } from '@solana/web3.js';
import { Wallet } from '@coral-xyz/anchor';
import dexterityTs from '@pepperdex/dexterity-ts';
const dexterity = dexterityTs;
// Solana RPC URL for connecting to the blockchain
const rpc = 'https://example-rpc.com';
// Setting up our wallet using a private key to sign transactions and interact with the blockchain
const keypair = Keypair.fromSecretKey(new Uint8Array([]));
const wallet = new Wallet(keypair);
/**
 * View account information and open orders for a specified trading group (TRG) in Dexterity.
 */
const viewAccount = async () => {
  // Fetch the latest manifest from Dexterity
  const manifest = await dexterity.getManifest(rpc, false, wallet);
  // Specify the Market-Product-Group (MPG) public key
  const MPG = new PublicKey('BRWNCEzQTm8kvEXHsVVY9jpb1VLbpv9B8mkF43nMLCtu');
  const trgPubkey = new PublicKey('your-trg'); // Replace with actual TRG public key
  console.log(`Wallet: ${wallet.publicKey.toBase58()} TRG: ${trgPubkey.toBase58()}`);
  // Define the product name to filter orders
  const PRODUCT_NAME = ['SOLUSD-PERP']; // Array of product names
  // Create a trader instance to interact with Dexterity
  const trader = new dexterity.Trader(manifest, trgPubkey);
  // Function to stream and display account and order information
  const streamAccount = async () => {
    // Retrieve all open orders for specified products
    const orders = await Promise.all(trader.getOpenOrders(PRODUCT_NAME));
    // Format and display open orders
    if (orders.length === 0) {
      console.log('No Open Orders');
    } else {
      orders.forEach((order, index) => {
        console.log(`Index: ${index} | Product: ${order.productName} | Price: $${order.price.m} | Qty: ${order.qty.m.toNumber() / 10 ** 6} | Type: ${order.isBid ? 'Bid' : 'Ask'} | Id: ${order.id.toString()}`);
      });
    }
    // Display account information
    console.log('\nOpen Orders:', orders.length, '\nPortfolio Value:', trader.getPortfolioValue().toString(), 'Position Value:', trader.getPositionValue().toString(), 'Net Cash:', trader.getNetCash().toString(), 'PnL:', trader.getPnL().toString());
  };
  // Connect to the trader account and stream account information
  await trader.connect(streamAccount, NaN);
};
viewAccount();6. Instructions Bundles
Code implementation:
import { clusterApiUrl, Keypair, PublicKey } from '@solana/web3.js';
import { Wallet } from '@coral-xyz/anchor';
import dexterityTs from '@pepperdex/dexterity-ts';
const dexterity = dexterityTs;
// Solana RPC URL for connecting to the blockchain
const rpc = 'https://example-rpc.com';
// Set up the wallet using a private key to sign transactions
const keypair = Keypair.fromSecretKey(new Uint8Array([])); // Replace with actual private key
const wallet = new Wallet(keypair);
// Initialize constants like the Trader Account (TRG) and Product Index
const TRG = new PublicKey(''); // Replace with actual TRG public key
const PRODUCT_INDEX = 0; // Index for 'SOLUSD-PERP' or similar product
/**
 * Demonstrates how to efficiently submit multiple instructions as a bundle.
 */
const submitIxBundle = async () => {
    // Fetch the latest manifest from Dexterity
    const manifest = await dexterity.getManifest(rpc, true, wallet);
    // Create a trader instance to interact with Dexterity
    const trader = new dexterity.Trader(manifest, TRG);
    // Connect to the trader account and log account details
    await trader.connect(NaN, async () => {
        console.log(`\nBALANCE: ${trader.getCashBalance()} | OPEN ORDERS: ${(await Promise.all(trader.getOpenOrders([PRODUCT_NAME]))).length} | POSITIONS VALUE: ${trader.getPositionValue()} | PNL: ${trader.getPnL()}`);
    });
    // Prepare order details using fractional values
    const sizeFractional = dexterity.Fractional.New(1, 0); // Replace with desired size
    const priceFractional = dexterity.Fractional.New(65, 0); // Replace with desired price
    // Define callback functions for handling order transactions
    const callbacks = {
        onGettingBlockHashFn: () => {},
        onGotBlockHashFn: () => {},
        onTxSentFn: (sig: string) => console.log(`SIGNATURE: https://solscan.io/tx/${sig}?cluster=devnet`)
    };
    // Generate the instruction for updating market prices
    const products = Array.from(dexterity.Manifest.GetProductsOfMPG(trader.mpg));
    const updateMarkIx = trader.getUpdateMarkPricesIx(products);
    // Generate the instruction for a new limit order
    const orderIx = trader.getNewOrderIx(PRODUCT_INDEX, false, priceFractional, sizeFractional, false, null, null, null, null);
    // Submit the bundled instructions
    await trader.sendTx([updateMarkIx, orderIx], callbacks);
};
submitIxBundle();