@triadxyz/triad-protocol
Version:
<div align="center"> <h1>Triad Protocol</h1> </div>
1,080 lines • 53.4 kB
JavaScript
"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