@frakters/nft-lending-v2
Version:
Client library for interacting with nft lenging solana program
755 lines (754 loc) • 44.4 kB
JavaScript
"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;