
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
@pepperdex/dexterity-ts
Advanced tools
This is a fork of [@hxronetwork/dexterity-ts](https://www.npmjs.com/package/@hxronetwork/dexterity-ts) updated and maintained by [PepperDEX](https://pepperdex.xyz/) Contributors
This is a fork of @hxronetwork/dexterity-ts updated and maintained by PepperDEX Contributors
mkdir dexterity-project
cd dexterity-project
npm i typescript --save-dev
npx tsc --init
npm install @solana/web3.js @pepperdex/dexterity-ts @coral-xyz/anchor
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 Object
class 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 Object
class 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>;
}
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();
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: $23
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
};
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();
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();
FAQs
This is a fork of [@hxronetwork/dexterity-ts](https://www.npmjs.com/package/@hxronetwork/dexterity-ts) updated and maintained by [PepperDEX](https://pepperdex.xyz/) Contributors
The npm package @pepperdex/dexterity-ts receives a total of 11 weekly downloads. As such, @pepperdex/dexterity-ts popularity was classified as not popular.
We found that @pepperdex/dexterity-ts demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.