UNPKG

@frakters/nft-lending-v2

Version:

Client library for interacting with nft lenging solana program

755 lines (754 loc) 44.4 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.settleFund = exports.checkUnsettledInfo = exports.memoInstruction = exports.transfer = exports.routeSwapOutInstruction = exports.routeSwapInInstruction = exports.swapInstruction = exports.place = exports.swapRouteOld = exports.swapRoute = exports.preSwapRoute = exports.swap = exports.forecastSell = exports.forecastBuy = exports.getSwapRouter = exports.getSwapOutAmountStable = exports.getSwapInAmount = exports.getSwapOutAmount = exports.getOutAmount = void 0; // @ts-ignore const buffer_layout_1 = require("buffer-layout"); const market_1 = require("@project-serum/serum/lib/market"); const token_instructions_1 = require("@project-serum/serum/lib/token-instructions"); const web3_js_1 = require("@solana/web3.js"); // eslint-disable-next-line const safe_math_1 = require("./safe-math"); const web3_1 = require("./web3"); const ids_1 = require("./ids"); const layouts_1 = require("./layouts"); // eslint-disable-next-line const tokens_1 = require("./tokens"); function getOutAmount(market, asks, bids, fromCoinMint, toCoinMint, amount, slippage) { const fromAmount = parseFloat(amount); let fromMint = fromCoinMint; let toMint = toCoinMint; if (fromMint === tokens_1.NATIVE_SOL.mintAddress) { fromMint = tokens_1.TOKENS.WSOL.mintAddress; } if (toMint === tokens_1.NATIVE_SOL.mintAddress) { toMint = tokens_1.TOKENS.WSOL.mintAddress; } if (fromMint === market.quoteMintAddress.toBase58() && toMint === market.baseMintAddress.toBase58()) { // buy return forecastBuy(market, asks, fromAmount, slippage); } else { return forecastSell(market, bids, fromAmount, slippage); } } exports.getOutAmount = getOutAmount; function getSwapOutAmount(poolInfo, fromCoinMint, toCoinMint, amount, slippage) { const { coin, pc, fees } = poolInfo; const { swapFeeNumerator, swapFeeDenominator } = fees; if (fromCoinMint === tokens_1.TOKENS.WSOL.mintAddress) fromCoinMint = tokens_1.NATIVE_SOL.mintAddress; if (toCoinMint === tokens_1.TOKENS.WSOL.mintAddress) toCoinMint = tokens_1.NATIVE_SOL.mintAddress; if (fromCoinMint === coin.mintAddress && toCoinMint === pc.mintAddress) { // coin2pc const fromAmount = new safe_math_1.TokenAmount(amount, coin.decimals, false); const fromAmountWithFee = fromAmount.wei .multipliedBy(swapFeeDenominator - swapFeeNumerator) .dividedBy(swapFeeDenominator); const denominator = coin.balance.wei.plus(fromAmountWithFee); const amountOut = pc.balance.wei.multipliedBy(fromAmountWithFee).dividedBy(denominator); const amountOutWithSlippage = amountOut.dividedBy(1 + slippage / 100); const outBalance = pc.balance.wei.minus(amountOut); const beforePrice = new safe_math_1.TokenAmount(parseFloat(new safe_math_1.TokenAmount(pc.balance.wei, pc.decimals).fixed()) / parseFloat(new safe_math_1.TokenAmount(coin.balance.wei, coin.decimals).fixed()), pc.decimals, false); const afterPrice = new safe_math_1.TokenAmount(parseFloat(new safe_math_1.TokenAmount(outBalance, pc.decimals).fixed()) / parseFloat(new safe_math_1.TokenAmount(denominator, coin.decimals).fixed()), pc.decimals, false); const priceImpact = Math.abs((parseFloat(beforePrice.fixed()) - parseFloat(afterPrice.fixed())) / parseFloat(beforePrice.fixed())) * 100; return { amountIn: fromAmount, amountOut: new safe_math_1.TokenAmount(amountOut, pc.decimals), amountOutWithSlippage: new safe_math_1.TokenAmount(amountOutWithSlippage, pc.decimals), priceImpact, }; } else { // pc2coin const fromAmount = new safe_math_1.TokenAmount(amount, pc.decimals, false); const fromAmountWithFee = fromAmount.wei .multipliedBy(swapFeeDenominator - swapFeeNumerator) .dividedBy(swapFeeDenominator); const denominator = pc.balance.wei.plus(fromAmountWithFee); const amountOut = coin.balance.wei.multipliedBy(fromAmountWithFee).dividedBy(denominator); const amountOutWithSlippage = amountOut.dividedBy(1 + slippage / 100); const outBalance = coin.balance.wei.minus(amountOut); const beforePrice = new safe_math_1.TokenAmount(parseFloat(new safe_math_1.TokenAmount(pc.balance.wei, pc.decimals).fixed()) / parseFloat(new safe_math_1.TokenAmount(coin.balance.wei, coin.decimals).fixed()), pc.decimals, false); const afterPrice = new safe_math_1.TokenAmount(parseFloat(new safe_math_1.TokenAmount(denominator, pc.decimals).fixed()) / parseFloat(new safe_math_1.TokenAmount(outBalance, coin.decimals).fixed()), pc.decimals, false); const priceImpact = Math.abs((parseFloat(afterPrice.fixed()) - parseFloat(beforePrice.fixed())) / parseFloat(beforePrice.fixed())) * 100; return { amountIn: fromAmount, amountOut: new safe_math_1.TokenAmount(amountOut, coin.decimals), amountOutWithSlippage: new safe_math_1.TokenAmount(amountOutWithSlippage, coin.decimals), priceImpact, }; } } exports.getSwapOutAmount = getSwapOutAmount; function getSwapInAmount(poolInfo, fromCoinMint, toCoinMint, amount, slippage) { const { coin, pc, fees } = poolInfo; const { swapFeeNumerator, swapFeeDenominator } = fees; const amountOut = parseFloat(amount); let amountIn = 0; let amountInWithFee = 0; let afterPrice = 0; const y = parseFloat(coin.balance.fixed()); const x = parseFloat(pc.balance.fixed()); const beforePrice = x / y; // (x+delta_x)*(y+delta_y)=x*y if (fromCoinMint === coin.mintAddress && toCoinMint === pc.mintAddress) { // coin2pc amountIn = (amountOut * y) / (x - amountOut); amountInWithFee = amountIn * (1 + swapFeeNumerator / swapFeeDenominator); afterPrice = (y + amountInWithFee) / (x - amountOut); } else { // pc2coin amountIn = (x * amountOut) / (y - amountOut); amountInWithFee = amountIn * (1 + swapFeeNumerator / swapFeeDenominator); afterPrice = (y - amountInWithFee) / (x + amountOut); } const amountInWithSlippage = amountInWithFee / (1 + slippage / 100); const priceImpact = Math.abs(((beforePrice - afterPrice) / beforePrice) * 100); return { amountIn: new safe_math_1.TokenAmount(amountIn * Math.pow(10, 6), 6), amountOut: new safe_math_1.TokenAmount(amountOut * Math.pow(10, 6), 6), amountOutWithSlippage: new safe_math_1.TokenAmount(amountInWithSlippage * Math.pow(10, pc.decimals), pc.decimals), priceImpact, }; } exports.getSwapInAmount = getSwapInAmount; function getSwapOutAmountStable(poolInfo, fromCoinMint, toCoinMint, amount, slippage) { const { coin, pc, fees, currentK } = poolInfo; const { swapFeeNumerator, swapFeeDenominator } = fees; const systemDecimal = Math.max(coin.decimals, pc.decimals); const k = currentK / (Math.pow(10, systemDecimal) * Math.pow(10, systemDecimal)); const amountIn = parseFloat(amount) * (1 - swapFeeNumerator / swapFeeDenominator); let amountOut = 1; const y = parseFloat(coin.balance.fixed()); const ammX = k / y; // (x+delta_x)*(y+delta_y)=x*y if (fromCoinMint === coin.mintAddress && toCoinMint === pc.mintAddress) { // coin2pc amountOut = ammX - k / (y + amountIn); } else { // pc2coin amountOut = y - k / (ammX + amountIn); } const beforePrice = Math.sqrt(((10 - 1) * y * y) / (10 * y * y - k)); const amountOutWithSlippage = amountOut / (1 + slippage / 100); const afterY = y - amountOut; const afterPrice = Math.sqrt(((10 - 1) * afterY * afterY) / (10 * afterY * afterY - k)); const priceImpact = ((beforePrice - afterPrice) / beforePrice) * 100; return { amountIn: new safe_math_1.TokenAmount(amountIn * Math.pow(10, 6), 6), amountOut: new safe_math_1.TokenAmount(amountOut * Math.pow(10, 6), 6), amountOutWithSlippage: new safe_math_1.TokenAmount(amountOutWithSlippage * Math.pow(10, pc.decimals), pc.decimals), priceImpact, }; } exports.getSwapOutAmountStable = getSwapOutAmountStable; function getSwapRouter(poolInfos, fromCoinMint, toCoinMint) { const routerCoinDefault = ['USDC', 'RAY', 'SOL', 'WSOL', 'mSOL', 'PAI']; const ret = []; const avaPools = []; for (const p of poolInfos) { if (!(p.version === 4 && p.status === 1)) continue; if ([fromCoinMint, toCoinMint].includes(p.coin.mintAddress) && routerCoinDefault.includes(p.pc.symbol)) { avaPools.push(p); } else if ([fromCoinMint, toCoinMint].includes(p.pc.mintAddress) && routerCoinDefault.includes(p.coin.symbol)) { avaPools.push(p); } } for (const p1 of avaPools) { if (p1.coin.mintAddress === fromCoinMint) { const poolInfo = avaPools.filter((p2) => p1.ammId !== p2.ammId && ((p2.pc.mintAddress === p1.pc.mintAddress && p2.coin.mintAddress === toCoinMint) || (p2.coin.mintAddress === p1.pc.mintAddress && p2.pc.mintAddress === toCoinMint))); for (const aP of poolInfo) { ret.push([p1, aP]); } } else if (p1.pc.mintAddress === fromCoinMint) { const poolInfo = avaPools.filter((p2) => p1.ammId !== p2.ammId && ((p2.pc.mintAddress === p1.coin.mintAddress && p2.coin.mintAddress === toCoinMint) || (p2.coin.mintAddress === p1.coin.mintAddress && p2.pc.mintAddress === toCoinMint))); for (const aP of poolInfo) { ret.push([p1, aP]); } } } return ret; } exports.getSwapRouter = getSwapRouter; function forecastBuy(market, orderBook, pcIn, slippage) { let coinOut = 0; let bestPrice = null; let worstPrice = 0; let availablePc = pcIn; for (const { key, quantity } of orderBook.items(false)) { const price = (market === null || market === void 0 ? void 0 : market.priceLotsToNumber(key.ushrn(64))) || 0; const size = (market === null || market === void 0 ? void 0 : market.baseSizeLotsToNumber(quantity)) || 0; if (!bestPrice && price !== 0) { bestPrice = price; } const orderPcVaule = price * size; worstPrice = price; if (orderPcVaule >= availablePc) { coinOut += availablePc / price; availablePc = 0; break; } else { coinOut += size; availablePc -= orderPcVaule; } } coinOut = coinOut * 0.993; const priceImpact = ((worstPrice - bestPrice) / bestPrice) * 100; worstPrice = (worstPrice * (100 + slippage)) / 100; const amountOutWithSlippage = (coinOut * (100 - slippage)) / 100; // const avgPrice = (pcIn - availablePc) / coinOut; const maxInAllow = pcIn - availablePc; return { side: 'buy', maxInAllow, amountOut: coinOut, amountOutWithSlippage, worstPrice, priceImpact, }; } exports.forecastBuy = forecastBuy; function forecastSell(market, orderBook, coinIn, slippage) { let pcOut = 0; let bestPrice = null; let worstPrice = 0; let availableCoin = coinIn; for (const { key, quantity } of orderBook.items(true)) { const price = market.priceLotsToNumber(key.ushrn(64)) || 0; const size = (market === null || market === void 0 ? void 0 : market.baseSizeLotsToNumber(quantity)) || 0; if (!bestPrice && price !== 0) { bestPrice = price; } worstPrice = price; if (availableCoin <= size) { pcOut += availableCoin * price; availableCoin = 0; break; } else { pcOut += price * size; availableCoin -= size; } } pcOut = pcOut * 0.993; const priceImpact = ((bestPrice - worstPrice) / bestPrice) * 100; worstPrice = (worstPrice * (100 - slippage)) / 100; const amountOutWithSlippage = (pcOut * (100 - slippage)) / 100; // const avgPrice = pcOut / (coinIn - availableCoin); const maxInAllow = coinIn - availableCoin; return { side: 'sell', maxInAllow, amountOut: pcOut, amountOutWithSlippage, worstPrice, priceImpact, }; } exports.forecastSell = forecastSell; function swap(connection, wallet, poolInfo, fromCoinMint, toCoinMint, fromTokenAccount, toTokenAccount, aIn, aOut, wsolAddress) { return __awaiter(this, void 0, void 0, function* () { const transaction = new web3_js_1.Transaction(); const signers = []; const owner = wallet.publicKey; const from = tokens_1.getTokenByMintAddress(fromCoinMint); const to = tokens_1.getTokenByMintAddress(toCoinMint); if (!from || !to) { throw new Error('Miss token info'); } const amountIn = new safe_math_1.TokenAmount(aIn, from.decimals, false); const amountOut = new safe_math_1.TokenAmount(aOut, to.decimals, false); if (fromCoinMint === tokens_1.NATIVE_SOL.mintAddress && wsolAddress) { transaction.add(token_instructions_1.closeAccount({ source: new web3_js_1.PublicKey(wsolAddress), destination: owner, owner, })); } let fromMint = fromCoinMint; let toMint = toCoinMint; if (fromMint === tokens_1.NATIVE_SOL.mintAddress) { fromMint = tokens_1.TOKENS.WSOL.mintAddress; } if (toMint === tokens_1.NATIVE_SOL.mintAddress) { toMint = tokens_1.TOKENS.WSOL.mintAddress; } let wrappedSolAccount = null; let wrappedSolAccount2 = null; let newFromTokenAccount = web3_js_1.PublicKey.default; let newToTokenAccount = web3_js_1.PublicKey.default; if (fromCoinMint === tokens_1.NATIVE_SOL.mintAddress) { wrappedSolAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, layouts_1.getBigNumber(amountIn.wei) + 1e7, transaction, signers); } else { newFromTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(fromTokenAccount, owner, fromMint, transaction); } if (toCoinMint === tokens_1.NATIVE_SOL.mintAddress) { wrappedSolAccount2 = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount2.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, 1e7, transaction, signers); } else { newToTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(toTokenAccount, owner, toMint, transaction); } transaction.add(swapInstruction(new web3_js_1.PublicKey(poolInfo.programId), new web3_js_1.PublicKey(poolInfo.ammId), new web3_js_1.PublicKey(poolInfo.ammAuthority), new web3_js_1.PublicKey(poolInfo.ammOpenOrders), new web3_js_1.PublicKey(poolInfo.ammTargetOrders), new web3_js_1.PublicKey(poolInfo.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfo.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfo.serumProgramId), new web3_js_1.PublicKey(poolInfo.serumMarket), new web3_js_1.PublicKey(poolInfo.serumBids), new web3_js_1.PublicKey(poolInfo.serumAsks), new web3_js_1.PublicKey(poolInfo.serumEventQueue), new web3_js_1.PublicKey(poolInfo.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfo.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfo.serumVaultSigner), wrappedSolAccount !== null && wrappedSolAccount !== void 0 ? wrappedSolAccount : newFromTokenAccount, wrappedSolAccount2 !== null && wrappedSolAccount2 !== void 0 ? wrappedSolAccount2 : newToTokenAccount, owner, Math.floor(layouts_1.getBigNumber(amountIn.toWei())), Math.floor(layouts_1.getBigNumber(amountOut.toWei())))); if (wrappedSolAccount) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount, destination: owner, owner, })); } if (wrappedSolAccount2) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount2, destination: owner, owner, })); } return yield web3_1.sendTransaction(connection, wallet, transaction, signers); }); } exports.swap = swap; function preSwapRoute(connection, wallet, fromMint, fromTokenAccount, middleMint, middleTokenAccount, toMint, toTokenAccount, needWrapAmount) { return __awaiter(this, void 0, void 0, function* () { const transaction = new web3_js_1.Transaction(); const signers = []; const owner = wallet.publicKey; console.log('needWrapAmount:', needWrapAmount); if (fromMint === tokens_1.TOKENS.WSOL.mintAddress || fromMint === tokens_1.NATIVE_SOL.mintAddress) { yield web3_1.createAtaSolIfNotExistAndWrap(connection, fromTokenAccount, owner, transaction, signers, needWrapAmount); } if (middleMint === tokens_1.NATIVE_SOL.mintAddress) middleMint = tokens_1.TOKENS.WSOL.mintAddress; if (toMint === tokens_1.NATIVE_SOL.mintAddress) toMint = tokens_1.TOKENS.WSOL.mintAddress; yield web3_1.createAssociatedTokenAccountIfNotExist(middleTokenAccount, owner, middleMint, transaction); yield web3_1.createAssociatedTokenAccountIfNotExist(toTokenAccount, owner, toMint, transaction); return yield web3_1.sendTransaction(connection, wallet, transaction, signers); }); } exports.preSwapRoute = preSwapRoute; function swapRoute(connection, wallet, poolInfoA, poolInfoB, routerInfo, fromTokenAccount, middleTokenAccount, toTokenAccount, aIn, aOut) { return __awaiter(this, void 0, void 0, function* () { const transaction = new web3_js_1.Transaction(); const owner = wallet.publicKey; const fromCoinMint = routerInfo.route[0].mintA; const toCoinMint = routerInfo.route[1].mintB; const from = tokens_1.getTokenByMintAddress(fromCoinMint); const middle = tokens_1.getTokenByMintAddress(routerInfo.middle_coin); const to = tokens_1.getTokenByMintAddress(toCoinMint); if (!from || !middle || !to) { throw new Error('Miss token info'); } const amountIn = new safe_math_1.TokenAmount(aIn, from.decimals, false); const amountOut = new safe_math_1.TokenAmount(aOut, to.decimals, false); let fromMint = fromCoinMint; let toMint = toCoinMint; let middleMint = routerInfo.middle_coin; if (fromMint === tokens_1.NATIVE_SOL.mintAddress) fromMint = tokens_1.TOKENS.WSOL.mintAddress; if (middleMint === tokens_1.NATIVE_SOL.mintAddress) middleMint = tokens_1.TOKENS.WSOL.mintAddress; if (toMint === tokens_1.NATIVE_SOL.mintAddress) toMint = tokens_1.TOKENS.WSOL.mintAddress; const newFromTokenAccount = new web3_js_1.PublicKey(fromTokenAccount); const newMiddleTokenAccount = new web3_js_1.PublicKey(middleTokenAccount); const newToTokenAccount = new web3_js_1.PublicKey(toTokenAccount); const { publicKey } = yield web3_1.findProgramAddress([new web3_js_1.PublicKey(poolInfoA.ammId).toBuffer(), new web3_js_1.PublicKey(middleMint).toBuffer(), owner.toBuffer()], new web3_js_1.PublicKey(ids_1.ROUTE_SWAP_PROGRAM_ID)); transaction.add(routeSwapInInstruction(new web3_js_1.PublicKey(ids_1.ROUTE_SWAP_PROGRAM_ID), new web3_js_1.PublicKey(ids_1.LIQUIDITY_POOL_PROGRAM_ID_V4), new web3_js_1.PublicKey(poolInfoA.ammId), new web3_js_1.PublicKey(poolInfoB.ammId), new web3_js_1.PublicKey(poolInfoA.ammAuthority), new web3_js_1.PublicKey(poolInfoA.ammOpenOrders), new web3_js_1.PublicKey(poolInfoA.ammTargetOrders), new web3_js_1.PublicKey(poolInfoA.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfoA.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfoA.serumProgramId), new web3_js_1.PublicKey(poolInfoA.serumMarket), new web3_js_1.PublicKey(poolInfoA.serumBids), new web3_js_1.PublicKey(poolInfoA.serumAsks), new web3_js_1.PublicKey(poolInfoA.serumEventQueue), new web3_js_1.PublicKey(poolInfoA.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfoA.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfoA.serumVaultSigner), newFromTokenAccount, newMiddleTokenAccount, publicKey, owner, Math.floor(layouts_1.getBigNumber(amountIn.toWei()))), routeSwapOutInstruction(new web3_js_1.PublicKey(ids_1.ROUTE_SWAP_PROGRAM_ID), new web3_js_1.PublicKey(ids_1.LIQUIDITY_POOL_PROGRAM_ID_V4), new web3_js_1.PublicKey(poolInfoA.ammId), new web3_js_1.PublicKey(poolInfoB.ammId), new web3_js_1.PublicKey(poolInfoB.ammAuthority), new web3_js_1.PublicKey(poolInfoB.ammOpenOrders), new web3_js_1.PublicKey(poolInfoB.ammTargetOrders), new web3_js_1.PublicKey(poolInfoB.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfoB.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfoB.serumProgramId), new web3_js_1.PublicKey(poolInfoB.serumMarket), new web3_js_1.PublicKey(poolInfoB.serumBids), new web3_js_1.PublicKey(poolInfoB.serumAsks), new web3_js_1.PublicKey(poolInfoB.serumEventQueue), new web3_js_1.PublicKey(poolInfoB.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfoB.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfoB.serumVaultSigner), newMiddleTokenAccount, newToTokenAccount, publicKey, owner, Math.floor(layouts_1.getBigNumber(amountOut.toWei())))); return yield web3_1.sendTransaction(connection, wallet, transaction); }); } exports.swapRoute = swapRoute; function swapRouteOld(connection, wallet, poolInfoA, poolInfoB, routerInfo, fromTokenAccount, middleTokenAccount, toTokenAccount, aIn, aMiddle, aOut) { return __awaiter(this, void 0, void 0, function* () { const transaction = new web3_js_1.Transaction(); const owner = wallet.publicKey; const fromCoinMint = routerInfo.route[0].mintA; const toCoinMint = routerInfo.route[1].mintB; const from = tokens_1.getTokenByMintAddress(fromCoinMint); const middle = tokens_1.getTokenByMintAddress(routerInfo.middle_coin); const to = tokens_1.getTokenByMintAddress(toCoinMint); if (!from || !middle || !to) { throw new Error('Miss token info'); } const amountIn = new safe_math_1.TokenAmount(aIn, from.decimals, false); const amountMiddle = new safe_math_1.TokenAmount(aMiddle, middle.decimals, false); const amountOut = new safe_math_1.TokenAmount(aOut, to.decimals, false); let fromMint = fromCoinMint; let toMint = toCoinMint; let middleMint = routerInfo.middle_coin; if (fromMint === tokens_1.NATIVE_SOL.mintAddress) { fromMint = tokens_1.TOKENS.WSOL.mintAddress; } if (middleMint === tokens_1.NATIVE_SOL.mintAddress) { middleMint = tokens_1.TOKENS.WSOL.mintAddress; } if (toMint === tokens_1.NATIVE_SOL.mintAddress) { toMint = tokens_1.TOKENS.WSOL.mintAddress; } let wrappedSolAccount = null; let wrappedSolAccount2 = null; let wrappedSolAccount3 = null; if (fromCoinMint === tokens_1.NATIVE_SOL.mintAddress) { wrappedSolAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, layouts_1.getBigNumber(amountIn.wei) + 1e7, transaction, []); } if (middleMint === tokens_1.NATIVE_SOL.mintAddress) { wrappedSolAccount2 = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount2.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, 1e7, transaction, []); } if (toCoinMint === tokens_1.NATIVE_SOL.mintAddress) { wrappedSolAccount3 = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount3.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, 1e7, transaction, []); } const newFromTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(fromTokenAccount, owner, fromMint, transaction); const newMiddleTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(middleTokenAccount, owner, middleMint, transaction); const newToTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(toTokenAccount, owner, toMint, transaction); transaction.add(swapInstruction(new web3_js_1.PublicKey(poolInfoA.programId), new web3_js_1.PublicKey(poolInfoA.ammId), new web3_js_1.PublicKey(poolInfoA.ammAuthority), new web3_js_1.PublicKey(poolInfoA.ammOpenOrders), new web3_js_1.PublicKey(poolInfoA.ammTargetOrders), new web3_js_1.PublicKey(poolInfoA.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfoA.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfoA.serumProgramId), new web3_js_1.PublicKey(poolInfoA.serumMarket), new web3_js_1.PublicKey(poolInfoA.serumBids), new web3_js_1.PublicKey(poolInfoA.serumAsks), new web3_js_1.PublicKey(poolInfoA.serumEventQueue), new web3_js_1.PublicKey(poolInfoA.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfoA.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfoA.serumVaultSigner), wrappedSolAccount !== null && wrappedSolAccount !== void 0 ? wrappedSolAccount : newFromTokenAccount, wrappedSolAccount2 !== null && wrappedSolAccount2 !== void 0 ? wrappedSolAccount2 : newMiddleTokenAccount, owner, Math.floor(layouts_1.getBigNumber(amountIn.toWei())), Math.floor(layouts_1.getBigNumber(amountMiddle.toWei()))), swapInstruction(new web3_js_1.PublicKey(poolInfoB.programId), new web3_js_1.PublicKey(poolInfoB.ammId), new web3_js_1.PublicKey(poolInfoB.ammAuthority), new web3_js_1.PublicKey(poolInfoB.ammOpenOrders), new web3_js_1.PublicKey(poolInfoB.ammTargetOrders), new web3_js_1.PublicKey(poolInfoB.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfoB.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfoB.serumProgramId), new web3_js_1.PublicKey(poolInfoB.serumMarket), new web3_js_1.PublicKey(poolInfoB.serumBids), new web3_js_1.PublicKey(poolInfoB.serumAsks), new web3_js_1.PublicKey(poolInfoB.serumEventQueue), new web3_js_1.PublicKey(poolInfoB.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfoB.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfoB.serumVaultSigner), wrappedSolAccount2 !== null && wrappedSolAccount2 !== void 0 ? wrappedSolAccount2 : newMiddleTokenAccount, wrappedSolAccount3 !== null && wrappedSolAccount3 !== void 0 ? wrappedSolAccount3 : newToTokenAccount, owner, Math.floor(layouts_1.getBigNumber(amountMiddle.toWei())), Math.floor(layouts_1.getBigNumber(amountOut.toWei())))); if (wrappedSolAccount) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount, destination: owner, owner, })); } if (wrappedSolAccount2) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount2, destination: owner, owner, })); } if (wrappedSolAccount3) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount3, destination: owner, owner, })); } return yield web3_1.sendTransaction(connection, wallet, transaction); }); } exports.swapRouteOld = swapRouteOld; function place(connection, wallet, market, asks, bids, fromCoinMint, toCoinMint, fromTokenAccount, toTokenAccount, amount, slippage) { return __awaiter(this, void 0, void 0, function* () { const forecastConfig = getOutAmount(market, asks, bids, fromCoinMint, toCoinMint, amount, slippage); const transaction = new web3_js_1.Transaction(); const signers = []; const owner = wallet.publicKey; const openOrdersAccounts = yield market.findOpenOrdersAccountsForOwner(connection, owner, 0); // const useFeeDiscountPubkey: PublicKey | null const openOrdersAddress = yield web3_1.createProgramAccountIfNotExist(connection, // @ts-ignore openOrdersAccounts.length === 0 ? null : openOrdersAccounts[0].address.toBase58(), owner, new web3_js_1.PublicKey(ids_1.SERUM_PROGRAM_ID_V3), null, market_1._OPEN_ORDERS_LAYOUT_V2, transaction, signers); let wrappedSolAccount = null; if (fromCoinMint === tokens_1.NATIVE_SOL.mintAddress) { let lamports; if (forecastConfig.side === 'buy') { lamports = Math.round(forecastConfig.worstPrice * forecastConfig.amountOut * 1.01 * web3_js_1.LAMPORTS_PER_SOL); if (openOrdersAccounts.length > 0) { lamports -= layouts_1.getBigNumber(openOrdersAccounts[0].quoteTokenFree); } } else { lamports = Math.round(forecastConfig.maxInAllow * web3_js_1.LAMPORTS_PER_SOL); if (openOrdersAccounts.length > 0) { lamports -= layouts_1.getBigNumber(openOrdersAccounts[0].baseTokenFree); } } lamports = Math.max(lamports, 0) + 1e7; wrappedSolAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount.toBase58(), owner, tokens_1.TOKENS.WSOL.mintAddress, lamports, transaction, signers); } transaction.add(market.makePlaceOrderInstruction(connection, { owner, payer: wrappedSolAccount !== null && wrappedSolAccount !== void 0 ? wrappedSolAccount : new web3_js_1.PublicKey(fromTokenAccount), // @ts-ignore side: forecastConfig.side, price: forecastConfig.worstPrice, size: forecastConfig.side === 'buy' ? parseFloat(forecastConfig.amountOut.toFixed(6)) : parseFloat(forecastConfig.maxInAllow.toFixed(6)), orderType: 'ioc', openOrdersAddressKey: openOrdersAddress, })); if (wrappedSolAccount) { transaction.add(token_instructions_1.closeAccount({ source: wrappedSolAccount, destination: owner, owner, })); } let fromMint = fromCoinMint; let toMint = toCoinMint; if (fromMint === tokens_1.NATIVE_SOL.mintAddress) { fromMint = tokens_1.TOKENS.WSOL.mintAddress; } if (toMint === tokens_1.NATIVE_SOL.mintAddress) { toMint = tokens_1.TOKENS.WSOL.mintAddress; } const newFromTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(fromTokenAccount, owner, fromMint, transaction); const newToTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(toTokenAccount, owner, toMint, transaction); const userAccounts = [newFromTokenAccount, newToTokenAccount]; if (market.baseMintAddress.toBase58() === toMint && market.quoteMintAddress.toBase58() === fromMint) { userAccounts.reverse(); } const baseTokenAccount = userAccounts[0]; const quoteTokenAccount = userAccounts[1]; let referrerQuoteWallet = null; if (market.supportsReferralFees) { const quoteToken = tokens_1.getTokenByMintAddress(market.quoteMintAddress.toBase58()); if (quoteToken === null || quoteToken === void 0 ? void 0 : quoteToken.referrer) { referrerQuoteWallet = new web3_js_1.PublicKey(quoteToken === null || quoteToken === void 0 ? void 0 : quoteToken.referrer); } } const settleTransactions = yield market.makeSettleFundsTransaction(connection, new market_1.OpenOrders(openOrdersAddress, { owner }, new web3_js_1.PublicKey(ids_1.SERUM_PROGRAM_ID_V3)), baseTokenAccount, quoteTokenAccount, referrerQuoteWallet); return yield web3_1.sendTransaction(connection, wallet, web3_1.mergeTransactions([transaction, settleTransactions.transaction]), [ ...signers, ...settleTransactions.signers, ]); }); } exports.place = place; function swapInstruction(programId, // tokenProgramId: PublicKey, // amm ammId, ammAuthority, ammOpenOrders, ammTargetOrders, poolCoinTokenAccount, poolPcTokenAccount, // serum serumProgramId, serumMarket, serumBids, serumAsks, serumEventQueue, serumCoinVaultAccount, serumPcVaultAccount, serumVaultSigner, // user userSourceTokenAccount, userDestTokenAccount, userOwner, amountIn, minAmountOut) { const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amountIn'), buffer_layout_1.nu64('minAmountOut')]); const keys = [ // spl token { pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // amm { pubkey: ammId, isSigner: false, isWritable: true }, { pubkey: ammAuthority, isSigner: false, isWritable: false }, { pubkey: ammOpenOrders, isSigner: false, isWritable: true }, { pubkey: ammTargetOrders, isSigner: false, isWritable: true }, { pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true }, { pubkey: poolPcTokenAccount, isSigner: false, isWritable: true }, // serum { pubkey: serumProgramId, isSigner: false, isWritable: false }, { pubkey: serumMarket, isSigner: false, isWritable: true }, { pubkey: serumBids, isSigner: false, isWritable: true }, { pubkey: serumAsks, isSigner: false, isWritable: true }, { pubkey: serumEventQueue, isSigner: false, isWritable: true }, { pubkey: serumCoinVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumPcVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumVaultSigner, isSigner: false, isWritable: false }, { pubkey: userSourceTokenAccount, isSigner: false, isWritable: true }, { pubkey: userDestTokenAccount, isSigner: false, isWritable: true }, { pubkey: userOwner, isSigner: true, isWritable: false }, ]; const data = Buffer.alloc(dataLayout.span); dataLayout.encode({ instruction: 9, amountIn, minAmountOut, }, data); return new web3_js_1.TransactionInstruction({ keys, programId, data, }); } exports.swapInstruction = swapInstruction; function routeSwapInInstruction(programId, ammProgramId, fromAmmId, toAmmId, ammAuthority, ammOpenOrders, _ammTargetOrders, poolCoinTokenAccount, poolPcTokenAccount, // serum serumProgramId, serumMarket, serumBids, serumAsks, serumEventQueue, serumCoinVaultAccount, serumPcVaultAccount, serumVaultSigner, // user userSourceTokenAccount, userMiddleTokenAccount, userPdaAccount, userOwner, amountIn) { const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amountIn')]); const keys = [ { pubkey: ids_1.SYSTEM_PROGRAM_ID, isSigner: false, isWritable: false }, // spl token { pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // amm { pubkey: ammProgramId, isSigner: false, isWritable: false }, { pubkey: fromAmmId, isSigner: false, isWritable: true }, { pubkey: toAmmId, isSigner: false, isWritable: true }, { pubkey: ammAuthority, isSigner: false, isWritable: false }, { pubkey: ammOpenOrders, isSigner: false, isWritable: true }, // { pubkey: ammTargetOrders, isSigner: false, isWritable: true }, { pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true }, { pubkey: poolPcTokenAccount, isSigner: false, isWritable: true }, // serum { pubkey: serumProgramId, isSigner: false, isWritable: false }, { pubkey: serumMarket, isSigner: false, isWritable: true }, { pubkey: serumBids, isSigner: false, isWritable: true }, { pubkey: serumAsks, isSigner: false, isWritable: true }, { pubkey: serumEventQueue, isSigner: false, isWritable: true }, { pubkey: serumCoinVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumPcVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumVaultSigner, isSigner: false, isWritable: false }, { pubkey: userSourceTokenAccount, isSigner: false, isWritable: true }, { pubkey: userMiddleTokenAccount, isSigner: false, isWritable: true }, { pubkey: userPdaAccount, isSigner: false, isWritable: true }, { pubkey: userOwner, isSigner: true, isWritable: false }, ]; const data = Buffer.alloc(dataLayout.span); dataLayout.encode({ instruction: 0, amountIn, }, data); return new web3_js_1.TransactionInstruction({ keys, programId, data, }); } exports.routeSwapInInstruction = routeSwapInInstruction; function routeSwapOutInstruction(programId, ammProgramId, fromAmmId, toAmmId, ammAuthority, ammOpenOrders, _ammTargetOrders, poolCoinTokenAccount, poolPcTokenAccount, // serum serumProgramId, serumMarket, serumBids, serumAsks, serumEventQueue, serumCoinVaultAccount, serumPcVaultAccount, serumVaultSigner, // user userMiddleTokenAccount, userDestTokenAccount, userPdaAccount, userOwner, amountOut) { const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amountOut')]); const keys = [ { pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // amm { pubkey: ammProgramId, isSigner: false, isWritable: false }, { pubkey: fromAmmId, isSigner: false, isWritable: true }, { pubkey: toAmmId, isSigner: false, isWritable: true }, { pubkey: ammAuthority, isSigner: false, isWritable: false }, { pubkey: ammOpenOrders, isSigner: false, isWritable: true }, // { pubkey: ammTargetOrders, isSigner: false, isWritable: true }, { pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true }, { pubkey: poolPcTokenAccount, isSigner: false, isWritable: true }, // serum { pubkey: serumProgramId, isSigner: false, isWritable: false }, { pubkey: serumMarket, isSigner: false, isWritable: true }, { pubkey: serumBids, isSigner: false, isWritable: true }, { pubkey: serumAsks, isSigner: false, isWritable: true }, { pubkey: serumEventQueue, isSigner: false, isWritable: true }, { pubkey: serumCoinVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumPcVaultAccount, isSigner: false, isWritable: true }, { pubkey: serumVaultSigner, isSigner: false, isWritable: false }, { pubkey: userMiddleTokenAccount, isSigner: false, isWritable: true }, { pubkey: userDestTokenAccount, isSigner: false, isWritable: true }, { pubkey: userPdaAccount, isSigner: false, isWritable: true }, { pubkey: userOwner, isSigner: true, isWritable: false }, ]; const data = Buffer.alloc(dataLayout.span); dataLayout.encode({ instruction: 1, amountOut, }, data); return new web3_js_1.TransactionInstruction({ keys, programId, data, }); } exports.routeSwapOutInstruction = routeSwapOutInstruction; function transfer(source, destination, owner, amount) { const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amount')]); const keys = [ { pubkey: source, isSigner: false, isWritable: true }, { pubkey: destination, isSigner: false, isWritable: true }, { pubkey: owner, isSigner: true, isWritable: false }, ]; const data = Buffer.alloc(dataLayout.span); dataLayout.encode({ instruction: 3, amount, }, data); return new web3_js_1.TransactionInstruction({ keys, programId: ids_1.TOKEN_PROGRAM_ID, data, }); } exports.transfer = transfer; function memoInstruction(memo) { return new web3_js_1.TransactionInstruction({ keys: [], data: Buffer.from(memo, 'utf-8'), programId: ids_1.MEMO_PROGRAM_ID, }); } exports.memoInstruction = memoInstruction; function checkUnsettledInfo(connection, wallet, market) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { if (!wallet) return; const owner = wallet.publicKey; if (!owner) return; const openOrderss = yield (market === null || market === void 0 ? void 0 : market.findOpenOrdersAccountsForOwner(connection, owner, 1000)); if (!(openOrderss === null || openOrderss === void 0 ? void 0 : openOrderss.length)) return; const baseTotalAmount = market.baseSplSizeToNumber(openOrderss[0].baseTokenTotal); const quoteTotalAmount = market.quoteSplSizeToNumber(openOrderss[0].quoteTokenTotal); const baseUnsettledAmount = market.baseSplSizeToNumber(openOrderss[0].baseTokenFree); const quoteUnsettledAmount = market.quoteSplSizeToNumber(openOrderss[0].quoteTokenFree); return { baseSymbol: (_a = tokens_1.getTokenByMintAddress(market.baseMintAddress.toString())) === null || _a === void 0 ? void 0 : _a.symbol, quoteSymbol: (_b = tokens_1.getTokenByMintAddress(market.quoteMintAddress.toString())) === null || _b === void 0 ? void 0 : _b.symbol, baseTotalAmount, quoteTotalAmount, baseUnsettledAmount, quoteUnsettledAmount, openOrders: openOrderss[0], }; }); } exports.checkUnsettledInfo = checkUnsettledInfo; function settleFund(connection, market, openOrders, wallet, baseMint, quoteMint, baseWallet, quoteWallet) { return __awaiter(this, void 0, void 0, function* () { const tx = new web3_js_1.Transaction(); const signs = []; const owner = wallet.publicKey; let wrappedBaseAccount; let wrappedQuoteAccount; if (baseMint === tokens_1.TOKENS.WSOL.mintAddress) { wrappedBaseAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedBaseAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, 1e7, tx, signs); } if (quoteMint === tokens_1.TOKENS.WSOL.mintAddress) { wrappedQuoteAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedQuoteAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, 1e7, tx, signs); } const quoteToken = tokens_1.getTokenByMintAddress(quoteMint); const { transaction, signers } = yield market.makeSettleFundsTransaction(connection, openOrders, wrappedBaseAccount !== null && wrappedBaseAccount !== void 0 ? wrappedBaseAccount : new web3_js_1.PublicKey(baseWallet), wrappedQuoteAccount !== null && wrappedQuoteAccount !== void 0 ? wrappedQuoteAccount : new web3_js_1.PublicKey(quoteWallet), quoteToken && quoteToken.referrer ? new web3_js_1.PublicKey(quoteToken.referrer) : null); if (wrappedBaseAccount) { transaction.add(token_instructions_1.closeAccount({ source: wrappedBaseAccount, destination: owner, owner, })); } if (wrappedQuoteAccount) { transaction.add(token_instructions_1.closeAccount({ source: wrappedQuoteAccount, destination: owner, owner, })); } return yield web3_1.sendTransaction(connection, wallet, web3_1.mergeTransactions([tx, transaction]), [...signs, ...signers]); }); } exports.settleFund = settleFund;