UNPKG

@thespidercode/openbook-swap

Version:
230 lines (229 loc) 9.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.marketOrdersInit = exports.updateMarketOrders = exports.computeQuotes = exports.getMarketData = exports.getMarketOrders = exports.getMarket = exports.getMarketInfo = exports.getMarketOrdersOnChain = void 0; const web3_js_1 = require("@solana/web3.js"); const dex_constant_1 = require("./constants/dex.constant"); const api_constant_1 = require("./constants/api.constant"); const market_1 = require("./serum/market"); const getMarketOrdersOnChain = async (marketAddress, connection, market) => { try { if (!market) { const programAddress = new web3_js_1.PublicKey(dex_constant_1.DEX_ADDRESS); market = await market_1.Market.load(connection, marketAddress, {}, programAddress); } return { market: await (0, exports.getMarketInfo)(connection, market) }; } catch (error) { throw (error); } }; exports.getMarketOrdersOnChain = getMarketOrdersOnChain; const getMarketInfo = async (connection, market) => { try { const [bids, asks] = await Promise.all([ market.loadBids(connection), market.loadAsks(connection) ]); const bidsL2 = bids.getL2(Number.MAX_VALUE).map((value) => [value[0], value[1]]); const asksL2 = asks.getL2(Number.MAX_VALUE).map((value) => [value[0], value[1]]); const marketInfo = { bids: bidsL2, asks: asksL2, totalBaseQuantity: asksL2.reduce((accum, current) => accum + current[1], 0), totalQuoteQuantity: bidsL2.reduce((accum, current) => accum + (current[1] * current[0]), 0), highestBid: bidsL2[0][0], highestBidVQuantity: bidsL2[0][1], lowestAsk: asksL2[0][0], lowestAskQuantity: asksL2[0][1], updatedAt: new Date().toLocaleTimeString() }; return marketInfo; } catch (error) { throw (error); } }; exports.getMarketInfo = getMarketInfo; const getMarket = async (marketAddress) => { try { return { data: await (await fetch(`${api_constant_1.API_BASE_URL}markets/info?address=${marketAddress}`, { headers: { "Content-Type": "application/json" } })).json() }; } catch (error) { console.log(error); return null; } }; exports.getMarket = getMarket; const getMarketOrders = async (marketAddress) => { try { return await (await (0, exports.getMarket)(marketAddress.toString()))?.data ?? null; } catch (error) { console.log(error); return null; } }; exports.getMarketOrders = getMarketOrders; const getMarketData = async (marketAddress, connection, onchain) => { let marketOrdersResponse; let isOnChainData = false; if (onchain) { marketOrdersResponse = await (0, exports.getMarketOrdersOnChain)(marketAddress, connection); } else { marketOrdersResponse = await (0, exports.getMarketOrders)(marketAddress); if (!marketOrdersResponse) { isOnChainData = true; } else if (isOnChainData) { isOnChainData = false; } } return { marketOrdersResponse, isOnChainData }; }; exports.getMarketData = getMarketData; const computeQuotes = async (marketOrders, swap) => { try { if (!marketOrders?.bids || !marketOrders?.asks) return; const baseAmount = parseFloat(swap.inputAmounts.base) ?? 0; const quoteAmount = parseFloat(swap.inputAmounts.quote) ?? 0; if (swap.sell && baseAmount > 0) { let amount = baseAmount; let value = 0; let bidIndex = 0; let totalVolume = 0; while (amount > 0 && bidIndex < marketOrders.bids.length) { const cost = (amount > marketOrders.bids[bidIndex][1] ? marketOrders.bids[bidIndex][1] : amount) * marketOrders.bids[bidIndex][0]; value += cost; amount -= marketOrders.bids[bidIndex][1]; totalVolume += marketOrders.bids[bidIndex][1]; bidIndex++; } if (amount > 0) { console.log('Market empty, max', totalVolume.toLocaleString(), swap.market.base.name); } return { ...swap, amounts: { ...swap.amounts, quote: marketOrders.totalQuoteQuantity < value ? marketOrders.totalQuoteQuantity * (1 - swap.market.swapMargin) : value * (1 - swap.market.swapMargin) }, inputAmounts: { ...swap.inputAmounts, base: amount > 0 ? totalVolume.toFixed(2) : swap.inputAmounts.base }, slotConsumed: bidIndex }; } else if (!swap.sell && quoteAmount > 0) { let amount = quoteAmount; let value = 0; let quoteIndex = 0; let totalCost = 0; while (amount > 0 && quoteIndex < marketOrders.asks.length) { // TODO: WHAT IS THE COST OF THE "ORDER STEP" ? const cost = marketOrders.asks[quoteIndex][1] * marketOrders.asks[quoteIndex][0]; value += amount > cost ? marketOrders.asks[quoteIndex][1] : (amount / cost) * marketOrders.asks[quoteIndex][1]; amount -= cost; totalCost += cost; quoteIndex++; } if (amount > 0) { console.log('Market empty, max', totalCost.toLocaleString(), swap.market.quote.name); } const newBase = Math.round((value * (1 - swap.market.swapMargin)) / 1000) * 1000; return { ...swap, amounts: { ...swap.amounts, base: marketOrders.totalBaseQuantity < newBase ? marketOrders.totalBaseQuantity : newBase, }, inputAmounts: { ...swap.inputAmounts, quote: amount > 0 ? totalCost.toFixed(2) : swap.inputAmounts.quote }, slotConsumed: quoteIndex }; } else { if (swap.sell) { return { ...swap, amounts: { ...swap.amounts, quote: 0 }, inputAmounts: { ...swap.inputAmounts, quote: "" } }; } else { return { ...swap, amounts: { ...swap.amounts, base: 0 }, inputAmounts: { ...swap.inputAmounts, base: "" } }; } } } catch (error) { console.log(error); return undefined; } }; exports.computeQuotes = computeQuotes; const updateMarketOrders = async (marketOrders, marketOrdersResponse, setMarketOrders, base, price) => { const bidDifference = Number(((Number(marketOrders?.highestBid) - Number(marketOrdersResponse?.market?.highestBid)) / Number(marketOrders?.highestBid))); const askDifference = Number(((Number(marketOrdersResponse?.market?.lowestAsk) - Number(marketOrders?.lowestAsk)) / Number(marketOrders?.lowestAsk))); let updateBids = false; let updateAsks = false; if (marketOrdersResponse != null && (marketOrders.asks.length == 0 || marketOrders.bids.length == 0) && marketOrdersResponse.market) { setMarketOrders(marketOrdersResponse.market); return; } if (marketOrdersResponse == null) { return; } const valueBase = Number(marketOrdersResponse?.market?.highestBid) * price; const valueQuote = Number(marketOrdersResponse?.market?.lowestAsk) * price; if ((bidDifference != 0 || Number(marketOrders?.highestBid) == 0) && ((valueQuote - valueBase) / valueQuote) < 0.1) { updateBids = true; } if ((askDifference != 0 || Number(marketOrders?.lowestAsk) == 0) && ((valueQuote - valueBase) / valueQuote) < 0.1) { updateAsks = true; } if (updateAsks && updateBids && marketOrdersResponse.market) { setMarketOrders(marketOrdersResponse.market); return; } else if (updateAsks && marketOrdersResponse?.market?.lowestAsk) { if (marketOrders != undefined && marketOrdersResponse?.market?.lowestAsk > 0) { setMarketOrders({ ...marketOrders, asks: marketOrdersResponse?.market?.asks ?? [], lowestAsk: marketOrdersResponse?.market?.lowestAsk, lowestAskQuantity: marketOrdersResponse.market?.lowestAskQuantity, totalBaseQuantity: marketOrdersResponse.market?.totalBaseQuantity, totalQuoteQuantity: marketOrdersResponse.market?.totalQuoteQuantity }); } } else if (updateBids && marketOrdersResponse?.market?.highestBid) { if (marketOrders != undefined && marketOrdersResponse?.market?.highestBid > 0) { setMarketOrders({ ...marketOrders, bids: marketOrdersResponse?.market?.bids ?? [], highestBid: marketOrdersResponse?.market?.highestBid ?? 0, highestBidVQuantity: marketOrdersResponse.market?.highestBidVQuantity ?? 0, totalBaseQuantity: marketOrdersResponse.market?.totalBaseQuantity ?? 0, totalQuoteQuantity: marketOrdersResponse.market?.totalQuoteQuantity ?? 0 }); } } }; exports.updateMarketOrders = updateMarketOrders; exports.marketOrdersInit = { bids: [], asks: [], totalBaseQuantity: 0, totalQuoteQuantity: 0, highestBid: 0, lowestAsk: 0, highestBidVQuantity: 0, lowestAskQuantity: 0, updatedAt: '' };