UNPKG

@triadxyz/triad-protocol

Version:

<div align="center"> <h1>Triad Protocol</h1> </div>

1,080 lines 53.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const web3_js_1 = require("@solana/web3.js"); const anchor_1 = require("@coral-xyz/anchor"); const spl_token_1 = require("@solana/spl-token"); const bn_js_1 = __importDefault(require("bn.js")); const helpers_1 = require("./utils/helpers"); const idl_triad_protocol_json_1 = __importDefault(require("./types/idl_triad_protocol.json")); const types_1 = require("./types"); const constants_1 = require("./utils/constants"); const helpers_2 = require("./utils/helpers"); const pda_1 = require("./utils/pda"); const sendVersionedTransaction_1 = __importDefault(require("./utils/sendVersionedTransaction")); const swap_1 = require("./utils/swap"); const feeCalculator_1 = require("./utils/feeCalculator"); const stake_1 = __importDefault(require("./stake")); const poseidon_1 = __importDefault(require("./poseidon")); const claim_1 = __importDefault(require("./claim")); __exportStar(require("./types"), exports); __exportStar(require("./utils/helpers"), exports); __exportStar(require("./utils/merkle"), exports); __exportStar(require("./utils/feeCalculator"), exports); class TriadProtocol { constructor(connection, wallet, rpcOptions) { this.connection = connection; this.wallet = wallet; this.rpcOptions = rpcOptions; if (!this.rpcOptions.payer) { this.rpcOptions.payer = this.wallet.publicKey; } this.provider = new anchor_1.AnchorProvider(this.connection, this.wallet, { commitment: this.rpcOptions.commitment || 'confirmed' }); this.program = new anchor_1.Program(idl_triad_protocol_json_1.default, this.provider); this.stake = new stake_1.default(this.program, this.rpcOptions); this.poseidon = new poseidon_1.default(this.program, this.rpcOptions); this.claim = new claim_1.default(this.program, this.rpcOptions); } /** * Get My User Trades from a user authority * @param wallet - User wallet PublicKey */ getMyUserTrades(wallet) { return __awaiter(this, void 0, void 0, function* () { const response = yield this.program.account.userTrade.all([ { memcmp: { offset: 8 + 1, bytes: wallet.toBase58() } } ]); return response.map(({ account, publicKey }) => (0, helpers_2.formatUserTrade)(account, publicKey)); }); } /** * Get User Orders * @param wallet - User wallet PublicKey */ getUserOrders(wallet) { return __awaiter(this, void 0, void 0, function* () { const myUserTrades = yield this.getMyUserTrades(wallet); return myUserTrades.flatMap((userTrade) => userTrade.orders); }); } /** * Get User Orders By Market ID * @param wallet - User wallet PublicKey * @param marketId - The ID of the market */ getUserOrdersByMarketId(wallet, marketId) { return __awaiter(this, void 0, void 0, function* () { const userTrades = yield this.getMyUserTrades(wallet); return userTrades.flatMap((userTrade) => userTrade.orders.filter((order) => order.marketId === marketId.toString())); }); } /** * Get all User Book Orders * @param wallet - User wallet PublicKey */ getUserBookOrders(wallet) { return __awaiter(this, void 0, void 0, function* () { const orderbooks = yield this.program.account.orderBook.all(); const bookOrders = orderbooks.map((orderbook) => { return [...orderbook.account.hypeOrders, ...orderbook.account.flopOrders] .map((order) => (0, helpers_2.formatBookOrder)(order, orderbook.account.marketId.toNumber())) .filter((order) => order.authority === wallet.toBase58() && order.linkedBookOrderId === constants_1.BOOK_ORDER_NULL.toString()); }); return bookOrders.flat(); }); } /** * Get User Book Orders By Market ID */ getUserBookOrdersByMarketId(wallet, marketId) { return __awaiter(this, void 0, void 0, function* () { const orderBook = yield this.program.account.orderBook.fetch((0, pda_1.getOrderBookPDA)(this.program.programId, marketId)); const orders = [...orderBook.hypeOrders, ...orderBook.flopOrders]; return orders .map((order) => (0, helpers_2.formatBookOrder)(order, marketId)) .filter((order) => order.authority === wallet.toBase58() && order.linkedBookOrderId === constants_1.BOOK_ORDER_NULL.toString()); }); } /** * Get Costumer By Wallet Address * @param wallet - The wallet address of the customer */ getCustomerByWallet(wallet) { return __awaiter(this, void 0, void 0, function* () { const [customer] = yield this.program.account.customer.all([ { memcmp: { offset: 10 + 1, bytes: wallet.toBase58() } } ]); return (0, helpers_2.formatCustomer)(customer.account, customer.publicKey); }); } /** * Get Customer By ID * @param customerId - The ID of the customer */ getCustomerById(customerId) { return __awaiter(this, void 0, void 0, function* () { const customerPDA = (0, pda_1.getCustomerPDA)(this.program.programId, customerId); const customer = yield this.program.account.customer.fetch(customerPDA); return (0, helpers_2.formatCustomer)(customer, customerPDA); }); } /** * Get User Trade PDA * @param wallet - User wallet PublicKey * @param userNonce - The nonce of the user */ getUserPDA(wallet, userNonce = 0) { let userTradePDA = (0, pda_1.getUserTradePDA)(this.program.programId, wallet); if (userNonce !== 0) { const subUserTradePDA = (0, pda_1.getSubUserTradePDA)(this.program.programId, wallet, userNonce); userTradePDA = (0, pda_1.getUserTradePDA)(this.program.programId, subUserTradePDA); } return userTradePDA; } /** * Get User Trade * @param wallet - User wallet PublicKey * @param userNonce - The nonce of the user */ getUserTradeByNonce(wallet, userNonce = 0) { return __awaiter(this, void 0, void 0, function* () { const userTradePDA = this.getUserPDA(wallet, userNonce); return this.program.account.userTrade.fetch(userTradePDA); }); } /** * Get All Pools */ getAllPools() { return __awaiter(this, void 0, void 0, function* () { const pool = yield this.program.account.pool.all(); return pool.map(({ account, publicKey }) => (0, helpers_1.formatPool)(account, publicKey)); }); } /** * Get All Markets */ getAllMarkets() { return __awaiter(this, void 0, void 0, function* () { const marketV2 = yield this.program.account.marketV2.all(); return marketV2.map(({ account, publicKey }) => (0, helpers_1.formatMarket)(account, publicKey)); }); } /** * Get Pool By ID * @param poolId - The ID of the pool */ getPoolById(poolId) { return __awaiter(this, void 0, void 0, function* () { const poolPDA = (0, pda_1.getPoolPDA)(this.program.programId, poolId); const response = yield this.program.account.pool.fetch(poolPDA); return (0, helpers_1.formatPool)(response, poolPDA); }); } /** * Get Market By ID * @param marketId - The ID of the market */ getMarketById(marketId) { return __awaiter(this, void 0, void 0, function* () { const marketPDA = (0, pda_1.getMarketPDA)(this.program.programId, marketId); const response = yield this.program.account.marketV2.fetch(marketPDA); return (0, helpers_1.formatMarket)(response, marketPDA); }); } /** * Get Market By Address * @param marketAddress - The address of the market */ getMarketByAddress(marketAddress) { return __awaiter(this, void 0, void 0, function* () { const account = yield this.program.account.marketV2.fetch(marketAddress); return (0, helpers_1.formatMarket)(account, marketAddress); }); } /** * Get Current Market ID */ nextMarketId() { return __awaiter(this, void 0, void 0, function* () { const markets = yield this.program.account.marketV2.all(); return markets.length + 10; }); } /** * Get Next Customer ID */ nextCustomerId() { return __awaiter(this, void 0, void 0, function* () { const customers = yield this.program.account.customer.all(); return customers.length + 1; }); } /** * Get Next Pool ID */ nextPoolId() { return __awaiter(this, void 0, void 0, function* () { const pools = yield this.program.account.pool.all(); return pools.length + 1; }); } /** * Create Market * @param args.markets - Array of markets to create * @param args.markets.marketId - Market ID * @param args.markets.startTime - start time * @param args.markets.endTime - end time * @param args.markets.question - question (max 80 characters) * @param args.markets.liquidityAtStart - liquidity at start * @param args.markets.payoutFee - payout fee (to add affiliate system) * @param args.customer - The customer of the market * @param args.poolId - The ID of the pool */ createMarket({ markets, customer, poolId }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; let poolPDA = null; if (poolId) { poolPDA = (0, pda_1.getPoolPDA)(this.program.programId, poolId); } for (const market of markets) { if (market.question.length > 80) { throw new Error('Question must be less than 80 characters'); } ixs.push(yield this.program.methods .createMarket({ marketId: new bn_js_1.default(market.marketId), question: (0, helpers_1.encodeString)(market.question, 80), marketStart: new bn_js_1.default(market.startTime), marketEnd: new bn_js_1.default(market.endTime), feeBps: market.feeBps, payoutFee: market.payoutFee }) .accounts({ signer: this.program.provider.publicKey, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, pool: poolPDA, customer }) .instruction()); ixs.push(yield this.program.methods .createOrderBook(new bn_js_1.default(market.marketId)) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, market.marketId) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Create Pool * @param poolId - The ID of the pool * @param question - The question of the pool * @param markets - The markets of the pool */ createPool({ poolId, question, markets, customer, startTime, endTime, feeBps, payoutFee, isFast }) { return __awaiter(this, void 0, void 0, function* () { if (question.length > 80) { throw new Error('Pool question must be less than 80 characters'); } const ixs = []; const poolPDA = (0, pda_1.getPoolPDA)(this.program.programId, poolId); ixs.push(yield this.program.methods .createPool({ poolId: new bn_js_1.default(poolId), question: (0, helpers_1.encodeString)(question, 80), isFast }) .accounts({ signer: this.program.provider.publicKey, customer }) .instruction()); for (const market of markets) { if (market.question.length > 80) { throw new Error('Market question must be less than 80 characters'); } ixs.push(yield this.program.methods .createMarket({ marketId: new bn_js_1.default(market.marketId), question: (0, helpers_1.encodeString)(market.question, 80), marketStart: new bn_js_1.default(startTime), marketEnd: new bn_js_1.default(endTime), feeBps, payoutFee }) .accounts({ signer: this.program.provider.publicKey, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, pool: poolPDA, customer }) .instruction()); ixs.push(yield this.program.methods .createOrderBook(new bn_js_1.default(market.marketId)) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, market.marketId) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Resolve Market * @param args.marketId - The ID of the Market * @param args.poolId - The ID of the Pool * @param args.winningDirection - The Winning Direction of the Market */ updateMarketWinningDirection({ marketId, poolId, winningDirection }) { return __awaiter(this, void 0, void 0, function* () { let poolPDA = null; if (poolId) { poolPDA = (0, pda_1.getPoolPDA)(this.program.programId, poolId); } const ixs = [ yield this.program.methods .updateMarketWinningDirection(winningDirection) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId), pool: poolPDA }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Update Market Payout * @param marketId - The ID of the market * @param poolId - The ID of the pool * @param allowPayout - Whether to allow the market to payout */ updateMarketPayout({ marketId, poolId, allowPayout }) { return __awaiter(this, void 0, void 0, function* () { let poolPDA = null; if (poolId) { poolPDA = (0, pda_1.getPoolPDA)(this.program.programId, poolId); } const ixs = [ yield this.program.methods .updateMarketPayout(allowPayout) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId) }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Update Market End * @param marketId - The ID of the market * @param marketEnd - The end time of the market */ updateMarketEnd({ marketId, marketEnd }) { return __awaiter(this, void 0, void 0, function* () { const ixs = [ yield this.program.methods .updateMarketEnd(new bn_js_1.default(marketEnd)) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId) }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Update Market Question * @param marketId - The ID of the market * @param question - The question of the market */ updateMarketQuestion({ marketId, question }) { return __awaiter(this, void 0, void 0, function* () { const ixs = [ yield this.program.methods .updateMarketQuestion((0, helpers_1.encodeString)(question, 80)) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId) }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Collect Market Fee * @param args.markets - The markets to collect the fee from * @param args.markets.marketAddress - The address of the market * @param args.markets.customerId - The ID of the customer * @param args.markets.customerFeeRecipient - The address of the customer fee recipient */ collectMarketFee(markets) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; for (const market of markets) { ixs.push(yield this.program.methods .collectMarketFee() .accounts({ signer: this.program.provider.publicKey, market: market.marketAddress, customer: (0, pda_1.getCustomerPDA)(this.program.programId, market.customerId), customerFeeRecipient: market.customerFeeRecipient }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Close Order Book * @param marketIds - Market IDs */ closeOrderBook(marketIds) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; for (const marketId of marketIds) { ixs.push(yield this.program.methods .closeOrderBook() .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, marketId) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Update Pool Question * @param poolId - Pool ID * @param question - Question */ updatePoolQuestion(poolId, question) { return __awaiter(this, void 0, void 0, function* () { const ixs = [ yield this.program.methods .updatePoolQuestion((0, helpers_1.encodeString)(question, 80)) .accounts({ signer: this.program.provider.publicKey, pool: (0, pda_1.getPoolPDA)(this.program.programId, poolId) }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Open Order * @param args.marketId - The ID of the Market * @param args.amount - The amount of the Order * @param args.direction - The direction of the Order * @param args.mint - The mint of the Order * @param args.token - The token to use for the Order */ openOrder({ marketId, amount, direction, mint, token }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; const addressLookupTableAccounts = []; const { userTradePDA, ixs: userTradeIxs } = yield this.getUserTradeIxs(); if (userTradeIxs.length > 0) { ixs.push(...userTradeIxs); } let amountInTRD = amount * Math.pow(10, constants_1.BASE_DECIMALS); if (token !== constants_1.TRD_MINT.toBase58()) { const { setupInstructions, swapIxs, addressLookupTableAccounts, outAmount } = yield (0, swap_1.swap)({ connection: this.program.provider.connection, wallet: this.program.provider.publicKey.toBase58(), inToken: token, outToken: constants_1.TRD_MINT.toString(), amount, payer: this.rpcOptions.payer.toBase58() }); amountInTRD = outAmount; if (swapIxs.length === 0) { return; } ixs.push(...setupInstructions); ixs.push(...swapIxs); addressLookupTableAccounts.push(...addressLookupTableAccounts); } ixs.push(yield this.program.methods .openOrder({ amount: new bn_js_1.default(amountInTRD), direction: direction }) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId), userTrade: userTradePDA, mint }) .instruction()); return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions, addressLookupTableAccounts); }); } /** * Close Order * @param args.marketId - The ID of the Market * @param args.orderId - The ID of the Order * @param args.userNonce - The nonce of the user */ closeOrder({ marketId, orderId, userNonce }) { return __awaiter(this, void 0, void 0, function* () { const userTrade = this.getUserPDA(this.program.provider.publicKey, userNonce); const ixs = [ yield this.program.methods .closeOrder(new bn_js_1.default(orderId)) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId), mint: constants_1.TRD_MINT, userTrade }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Payout Order * @param args.marketId - The ID of the Market * @param args.orderId - The ID of the Order to Payout * @param args.userNonce - The nonce of the user * @param args.mint - The mint of the market */ payoutOrder(orders) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; if (orders.length > 6) { throw new Error('Max 6 orders per transaction'); } for (const order of orders) { ixs.push(yield this.program.methods .payoutOrder(new bn_js_1.default(order.orderId)) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer, userTrade: this.getUserPDA(this.program.provider.publicKey, order.userNonce), market: (0, pda_1.getMarketPDA)(this.program.programId, order.marketId), mint: order.mint, tokenProgram: (0, helpers_2.getTokenProgram)(order.mint) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Create Sub User Trade * @param user - User PublicKey the main user */ createSubUserTrade(user) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; const userTrade = yield this.getUserTradeByNonce(user); const subUserTradePDA = (0, pda_1.getSubUserTradePDA)(this.program.programId, user, userTrade.nonce + 1); ixs.push(yield this.program.methods .createSubUserTrade(subUserTradePDA) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer }) .instruction()); return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Create Customer * @param args.id - The ID of the customer * @param args.name - The name of the customer * @param args.authority - The authority of the customer * @param args.feeRecipient - The fee recipient of the customer */ createCustomer({ id, name, authority, feeRecipient }) { return __awaiter(this, void 0, void 0, function* () { const ixs = [ yield this.program.methods .createCustomer({ id, name, authority, feeRecipient }) .accounts({ signer: this.program.provider.publicKey }) .instruction() ]; return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Get User Trade Ixs */ getUserTradeIxs() { return __awaiter(this, void 0, void 0, function* () { const ixs = []; let myUserTrades = []; myUserTrades = yield this.getMyUserTrades(this.program.provider.publicKey); if (myUserTrades.length === 0) { ixs.push(yield this.program.methods .createUserTrade() .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer }) .instruction()); return { userTradePDA: this.getUserPDA(this.program.provider.publicKey), ixs, nonce: 0 }; } try { let nonce = null; for (const userTrade of myUserTrades) { if (nonce !== null) { break; } userTrade.orders.forEach((order) => { if (order.orderStatus !== types_1.OrderStatus.OPEN) { nonce = userTrade.isSubUser ? Number(userTrade.nonce) : 0; } }); } if (nonce === null) { throw new Error('No open orders found'); } return { userTradePDA: this.getUserPDA(this.program.provider.publicKey, nonce), ixs }; } catch (_a) { const mainUserTrade = myUserTrades.find((trade) => !trade.isSubUser); const subUserTradePDA = (0, pda_1.getSubUserTradePDA)(this.program.programId, this.program.provider.publicKey, Number(mainUserTrade.nonce) + 1); ixs.push(yield this.program.methods .createSubUserTrade(subUserTradePDA) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer }) .instruction()); return { userTradePDA: (0, pda_1.getUserTradePDA)(this.program.programId, subUserTradePDA), ixs }; } }); } /** * Get User Trade Nonce * @param marketId - The ID of the Market * @param orderDirection - The direction of the Order */ getUserTradeNonce(marketId, orderDirection) { var _a; return __awaiter(this, void 0, void 0, function* () { const userOrders = yield this.getUserOrdersByMarketId(this.program.provider.publicKey, marketId); const userNonce = (_a = userOrders.find((order) => order.orderDirection === orderDirection && order.orderStatus === types_1.OrderStatus.OPEN && Number(order.marketId) === marketId)) === null || _a === void 0 ? void 0 : _a.userNonce; let userTradePDA = null; let userTradeIxs = []; if (userNonce) { userTradePDA = this.getUserPDA(this.program.provider.publicKey, Number(userNonce)); } if (!userNonce) { const { userTradePDA: user, ixs: ixsUser } = yield this.getUserTradeIxs(); userTradePDA = user; userTradeIxs = ixsUser; } return { userTradePDA, userTradeIxs }; }); } /** * Place Bid Order * @param args.orders - Array of orders to execute * @param args.orders.marketId - The ID of the Market * @param args.orders.amount - The amount of the Order * @param args.orders.price - The price of the Order * @param args.orders.orderDirection - The direction of the Order * @param args.mint - The mint of the Order * @param args.isTrdPayout - Whether to payout in TRD or not */ placeBidOrder({ orders, isTrdPayout }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; const addressLookupTableAccounts = []; if (orders.length > 4) { throw new Error('You can only place up to 4 orders at a time'); } let amountInUSDC = new bn_js_1.default(0); let totalAmount = 0; for (const order of orders) { totalAmount += order.amount; } if (isTrdPayout) { const trdPrice = yield (0, swap_1.getPrice)(constants_1.TRD_MINT.toString()); const amountInTRD = (totalAmount / trdPrice) * 1.06; const { swapIxs, addressLookupTableAccounts: swapAddressLookupTableAccounts, outAmount, setupInstructions } = yield (0, swap_1.swap)({ connection: this.program.provider.connection, wallet: this.program.provider.publicKey.toBase58(), inToken: constants_1.TRD_MINT.toString(), outToken: constants_1.USDC_MINT.toString(), amount: Math.floor(amountInTRD), payer: this.rpcOptions.payer.toBase58() }); if (swapIxs.length === 0) { return; } amountInUSDC = new bn_js_1.default(outAmount); ixs.push(...setupInstructions); ixs.push(...swapIxs); addressLookupTableAccounts.push(...swapAddressLookupTableAccounts); if (amountInUSDC.lt(new bn_js_1.default(totalAmount * Math.pow(10, constants_1.BASE_DECIMALS)))) { return; } } const { userTradePDA, userTradeIxs } = yield this.getUserTradeNonce(orders[0].marketId, Object.keys(orders[0].orderDirection)[0]); if (userTradeIxs.length > 0) { ixs.push(...userTradeIxs); } for (const order of orders) { ixs.push(yield this.program.methods .placeBidOrder({ amount: new bn_js_1.default(order.amount * Math.pow(10, constants_1.BASE_DECIMALS)), price: new bn_js_1.default(order.price * Math.pow(10, constants_1.BASE_DECIMALS)), orderDirection: order.orderDirection }) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, order.marketId), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, order.marketId), userTrade: userTradePDA }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Place Ask Order * @param args.marketId - The ID of the Market * @param args.orders - Array of orders to execute * @param args.orders.amount - The amount of the Order * @param args.orders.price - The price of the Order * @param args.orders.bidOrderId - The ID of the Bid Order * @param args.orders.bidNonce - The nonce of the Bid Order */ placeAskOrder({ orders }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; if (orders.length > 5) { throw new Error('You can only place up to 5 orders at a time'); } for (const order of orders) { ixs.push(yield this.program.methods .placeAskOrder({ shares: new bn_js_1.default(order.amount * Math.pow(10, constants_1.BASE_DECIMALS)), price: new bn_js_1.default(order.price * Math.pow(10, constants_1.BASE_DECIMALS)), bidOrderId: new bn_js_1.default(order.bidOrderId) }) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, order.marketId), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, order.marketId), userTrade: this.getUserPDA(this.program.provider.publicKey, order.userNonce) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Cancel Bid Order * @param args.orders.orderId - The ID of the Order * @param args.orders.userNonce - The nonce of the user * @param args.orders.orderDirection - The direction of the Order */ cancelBidOrder({ orders }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; if (orders.length > 5) { throw new Error('You can only cancel up to 5 orders at a time'); } for (const order of orders) { ixs.push(yield this.program.methods .cancelBidOrder({ orderId: new bn_js_1.default(order.orderId), orderDirection: order.orderDirection }) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer, market: (0, pda_1.getMarketPDA)(this.program.programId, order.marketId), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, order.marketId), userTrade: this.getUserPDA(this.program.provider.publicKey, order.userNonce) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Cancel Ask Order * @param args.marketId - The ID of the Market * @param args.orders.orderId - The ID of the Order * @param args.orders.userNonce - The nonce of the user * @param args.orders.orderDirection - The direction of the Order */ cancelAskOrder({ marketId, orders }) { return __awaiter(this, void 0, void 0, function* () { const ixs = []; if (orders.length > 4) { throw new Error('You can only cancel up to 4 orders at a time'); } for (const order of orders) { ixs.push(yield this.program.methods .cancelAskOrder({ orderId: new bn_js_1.default(order.orderId), orderDirection: order.orderDirection }) .accounts({ signer: this.program.provider.publicKey, market: (0, pda_1.getMarketPDA)(this.program.programId, marketId), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, marketId), userTrade: this.getUserPDA(this.program.provider.publicKey, order.userNonce) }) .instruction()); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions); }); } /** * Market Bid Order * @param args.marketId - The ID of the Market * @param args.amount - The amount of the Order * @param args.orderDirection - The direction of the Order * @param args.isTrdPayout - Whether to payout in TRD or not */ marketBidOrder({ marketId, amount, orderDirection, isTrdPayout }) { return __awaiter(this, void 0, void 0, function* () { const marketPDA = (0, pda_1.getMarketPDA)(this.program.programId, marketId); const ixs = []; const addressLookupTableAccounts = []; const { userTradePDA, userTradeIxs } = yield this.getUserTradeNonce(marketId, Object.keys(orderDirection)[0]); const orderBook = yield this.getOrderBook(marketId); let remainingUSDC = new bn_js_1.default(amount * Math.pow(10, constants_1.BASE_DECIMALS)); if (isTrdPayout) { const price = yield (0, swap_1.getPrice)(constants_1.TRD_MINT.toString()); const trdToUsdc = amount * price; remainingUSDC = new bn_js_1.default(trdToUsdc * Math.pow(10, constants_1.BASE_DECIMALS)); } const orders = Object.keys(orderDirection)[0] === 'hype' ? orderBook.hype.ask : orderBook.flop.ask; const sortedOrders = orders.sort((a, b) => Number(a.price) - Number(b.price)); let totalUSDCNeeded = new bn_js_1.default(0); let tempRemainingUSDC = remainingUSDC.clone(); if (isTrdPayout) { for (const order of sortedOrders) { if (tempRemainingUSDC.lte(new bn_js_1.default(0))) break; if (order.authority === this.program.provider.publicKey.toBase58()) { continue; } const orderPrice = new bn_js_1.default(order.price); const availableShares = new bn_js_1.default(order.totalShares).sub(new bn_js_1.default(order.filledShares)); const effectivePriceDecimal = (0, feeCalculator_1.applyBuyFee)(orderPrice.toNumber() / 1000000); const adjustedPrice = new bn_js_1.default(Math.floor(effectivePriceDecimal * 1000000)); const maxSharesForPrice = tempRemainingUSDC .mul(new bn_js_1.default(Math.pow(10, constants_1.BASE_DECIMALS))) .div(adjustedPrice); const sharesToBuy = bn_js_1.default.min(maxSharesForPrice, availableShares); if (sharesToBuy.lte(new bn_js_1.default(0))) continue; const usdcAmount = sharesToBuy .mul(adjustedPrice) .div(new bn_js_1.default(Math.pow(10, constants_1.BASE_DECIMALS))); if (usdcAmount.lte(new bn_js_1.default(0))) continue; totalUSDCNeeded = totalUSDCNeeded.add(usdcAmount); tempRemainingUSDC = tempRemainingUSDC.sub(usdcAmount); } const price = yield (0, swap_1.getPrice)(constants_1.TRD_MINT.toString()); const amountInTRD = totalUSDCNeeded.toNumber() / (price * Math.pow(10, constants_1.BASE_DECIMALS)); const { swapIxs, addressLookupTableAccounts: swapAddressLookupTableAccounts, outAmount, setupInstructions } = yield (0, swap_1.swap)({ connection: this.program.provider.connection, wallet: this.program.provider.publicKey.toBase58(), inToken: constants_1.TRD_MINT.toString(), outToken: constants_1.USDC_MINT.toString(), amount: Math.floor(amountInTRD), payer: this.rpcOptions.payer.toBase58() }); if (swapIxs.length === 0) { return; } remainingUSDC = new bn_js_1.default(outAmount); ixs.push(...setupInstructions); ixs.push(...swapIxs); addressLookupTableAccounts.push(...swapAddressLookupTableAccounts); } for (const order of sortedOrders) { if (remainingUSDC.lte(new bn_js_1.default(0))) break; if (order.authority === this.program.provider.publicKey.toBase58()) { continue; } const orderPrice = new bn_js_1.default(order.price); const availableShares = new bn_js_1.default(order.totalShares).sub(new bn_js_1.default(order.filledShares)); const effectivePriceDecimal = (0, feeCalculator_1.applyBuyFee)(orderPrice.toNumber() / 1000000); const adjustedPrice = new bn_js_1.default(Math.floor(effectivePriceDecimal * 1000000)); const maxSharesForPrice = remainingUSDC .mul(new bn_js_1.default(Math.pow(10, constants_1.BASE_DECIMALS))) .div(adjustedPrice); const sharesToBuy = bn_js_1.default.min(maxSharesForPrice, availableShares); if (sharesToBuy.lte(new bn_js_1.default(0))) continue; const usdcAmount = sharesToBuy .mul(adjustedPrice) .div(new bn_js_1.default(Math.pow(10, constants_1.BASE_DECIMALS))); if (usdcAmount.lte(new bn_js_1.default(0))) continue; ixs.push(yield this.program.methods .marketBidOrder({ amount: new bn_js_1.default(usdcAmount), orderDirection, askOrderId: new bn_js_1.default(order.id), isTrdPayout }) .accounts({ signer: this.program.provider.publicKey, market: marketPDA, buyerTrade: userTradePDA, orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, marketId), sellerAuthority: new web3_js_1.PublicKey(order.authority), sellerTrade: this.getUserPDA(new web3_js_1.PublicKey(order.authority), Number(order.userNonce)), marketAta: (0, pda_1.getTokenATA)(marketPDA, constants_1.USDC_MINT, spl_token_1.TOKEN_PROGRAM_ID), buyerAta: (0, pda_1.getTokenATA)(this.program.provider.publicKey, constants_1.USDC_MINT, spl_token_1.TOKEN_PROGRAM_ID) }) .instruction()); remainingUSDC = remainingUSDC.sub(usdcAmount); } if (userTradeIxs.length > 0) { ixs.unshift(...userTradeIxs); } if (ixs.length === 0) { throw new Error('No matching orders found to fill the requested amount'); } return (0, sendVersionedTransaction_1.default)(this.program, ixs, this.rpcOptions, addressLookupTableAccounts); }); } /** * Market Ask Order * @param args.marketId - The ID of the Market * @param args.orders - Array of orders to execute * @param args.orders.shares - The amount of shares to sell * @param args.orders.bidOrderId - The ID of the Bid Order * @param args.orders.userNonce - The nonce of the user * @param args.orderDirection - The direction of the Order * @param args.isTrdPayout - Whether to payout in TRD or not */ marketAskOrder({ marketId, orders, orderDirection, isTrdPayout }) { return __awaiter(this, void 0, void 0, function* () { const marketPDA = (0, pda_1.getMarketPDA)(this.program.programId, marketId); const ixs = []; const addressLookupTableAccounts = []; const orderBook = yield this.getOrderBook(marketId); const bidOrders = Object.keys(orderDirection)[0] === 'hype' ? orderBook.hype.bid : orderBook.flop.bid; const sortedOrders = bidOrders.sort((a, b) => Number(b.price) - Number(a.price)); let amountOfUSDC = new bn_js_1.default(0); for (const inputOrder of orders) { let remainingShares = new bn_js_1.default(inputOrder.shares * Math.pow(10, constants_1.BASE_DECIMALS)); for (const order of sortedOrders) { if (remainingShares.lte(new bn_js_1.default(0))) break; if (order.authority === this.program.provider.publicKey.toBase58() || order.linkedBookOrderId !== constants_1.BOOK_ORDER_NULL.toString()) { continue; } const availableShares = new bn_js_1.default(order.totalShares).sub(new bn_js_1.default(order.filledShares)); const sharesToSell = bn_js_1.default.min(remainingShares, availableShares); if (sharesToSell.lt(new bn_js_1.default(0))) continue; remainingShares = remainingShares.sub(sharesToSell); const orderPrice = new bn_js_1.default(order.price); const effectivePriceDecimal = (0, feeCalculator_1.applySellFee)(orderPrice.toNumber() / 1000000); const adjustedPrice = new bn_js_1.default(Math.floor(effectivePriceDecimal * 1000000)); amountOfUSDC = amountOfUSDC.add(sharesToSell.mul(adjustedPrice).div(new bn_js_1.default(Math.pow(10, constants_1.BASE_DECIMALS)))); ixs.push(yield this.program.methods .marketAskOrder({ shares: new bn_js_1.default(sharesToSell), orderDirection, bidOrderId: new bn_js_1.default(inputOrder.bidOrderId), bookOrderBidId: new bn_js_1.default(order.id) }) .accounts({ signer: this.program.provider.publicKey, payer: this.rpcOptions.payer, market: marketPDA, buyerAuthority: new web3_js_1.PublicKey(order.authority), buyerTrade: this.getUserPDA(new web3_js_1.PublicKey(order.authority), Number(order.userNonce)), orderBook: (0, pda_1.getOrderBookPDA)(this.program.programId, marketId), marketAta: (0, pda_1.getTokenATA)(marketPDA, constants_1.USDC_MINT, spl_token_1.TOKEN_PROGRAM_ID), sellerTrade: this.getUserPDA(this.program.provider.publicKey, inputOrder.userNonce) }) .instruction()); } } if (isTrdPayout) { const { setupInstructions, swapIxs, addressLookupTableAccounts: sw