@frakters/nft-lending-v2
Version:
Client library for interacting with nft lenging solana program
879 lines (878 loc) • 46.3 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());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.requestInfos = exports.getLiquidityInfo = exports.getLiquidityInfoSimilar = exports.getLpMintListDecimals = exports.getLpMintInfo = exports.AMM_INFO_LAYOUT_STABLE = exports.AMM_INFO_LAYOUT_V4 = exports.AMM_INFO_LAYOUT_V3 = exports.AMM_INFO_LAYOUT = exports.removeLiquidityInstructionV4 = exports.removeLiquidityInstruction = exports.addLiquidityInstructionV4 = exports.addLiquidityInstruction = exports.removeLiquidity = exports.addLiquidity = exports.getOutAmountStable = exports.getOutAmount = exports.getPrice = exports.getPoolByTokenMintAddresses = exports.getPoolByLpMintAddress = exports.getLpMintByTokenMintAddresses = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
// @ts-ignore
const buffer_layout_1 = require("buffer-layout");
const borsh_1 = require("@project-serum/borsh");
const token_instructions_1 = require("@project-serum/serum/lib/token-instructions");
const web3_js_1 = require("@solana/web3.js");
const ids_1 = require("./ids");
const pools_1 = require("./pools");
Object.defineProperty(exports, "getLpMintByTokenMintAddresses", { enumerable: true, get: function () { return pools_1.getLpMintByTokenMintAddresses; } });
Object.defineProperty(exports, "getPoolByLpMintAddress", { enumerable: true, get: function () { return pools_1.getPoolByLpMintAddress; } });
Object.defineProperty(exports, "getPoolByTokenMintAddresses", { enumerable: true, get: function () { return pools_1.getPoolByTokenMintAddresses; } });
const safe_math_1 = require("./safe-math");
const tokens_1 = require("./tokens");
const web3_1 = require("./web3");
const layouts_1 = require("./layouts");
const serum_1 = require("@project-serum/serum");
const lodash_1 = require("lodash");
function getPrice(poolInfo, coinBase = true) {
const { coin, pc } = poolInfo;
if (!coin.balance || !pc.balance) {
return new bignumber_js_1.default(0);
}
if (poolInfo.version === 5) {
const { currentK = 1 } = poolInfo;
const systemDecimal = Math.max(coin.decimals, pc.decimals);
const k = currentK / (Math.pow(10, systemDecimal) * Math.pow(10, systemDecimal));
const y = parseFloat(coin.balance.fixed());
let price = Math.sqrt(((10 - 1) * y * y) / (10 * y * y - k));
if (!coinBase)
price = 1 / price;
return new bignumber_js_1.default(price);
}
else if (coinBase) {
return pc.balance.toEther().dividedBy(coin.balance.toEther());
}
else {
return coin.balance.toEther().dividedBy(pc.balance.toEther());
}
}
exports.getPrice = getPrice;
function getOutAmount(poolInfo, amount, fromCoinMint, toCoinMint, slippage) {
const { coin, pc } = poolInfo;
const price = getPrice(poolInfo);
const fromAmount = new bignumber_js_1.default(amount);
let outAmount = new bignumber_js_1.default(0);
const percent = new bignumber_js_1.default(100).plus(slippage).dividedBy(100);
if (!coin.balance || !pc.balance) {
return outAmount;
}
if (fromCoinMint === coin.mintAddress && toCoinMint === pc.mintAddress) {
// outcoin is pc
outAmount = fromAmount.multipliedBy(price);
outAmount = outAmount.multipliedBy(percent);
}
else if (fromCoinMint === pc.mintAddress && toCoinMint === coin.mintAddress) {
// outcoin is coin
outAmount = fromAmount.dividedBy(price);
outAmount = outAmount.multipliedBy(percent);
}
return outAmount;
}
exports.getOutAmount = getOutAmount;
function getOutAmountStable(poolInfo, amount, fromCoinMint, toCoinMint, slippage) {
const { coin, pc, currentK } = poolInfo;
const systemDecimal = Math.max(coin.decimals, pc.decimals);
const k = currentK / (Math.pow(10, systemDecimal) * Math.pow(10, systemDecimal));
const y = parseFloat(coin.balance.fixed());
const price = Math.sqrt(((10 - 1) * y * y) / (10 * y * y - k));
const amountIn = parseFloat(amount);
let amountOut = 1;
if (fromCoinMint === coin.mintAddress && toCoinMint === pc.mintAddress) {
// outcoin is pc
amountOut = amountIn * price;
}
else if (fromCoinMint === pc.mintAddress && toCoinMint === coin.mintAddress) {
// outcoin is coin
amountOut = amountIn / price;
}
const amountOutWithSlippage = amountOut / (1 - slippage / 100);
// const price = Math.sqrt((10 - 1) * y * y /(10 * y * y - k))
// const afterY = y - amountOut
// const afterPrice = Math.sqrt((10 - 1) * afterY * afterY /(10 * afterY * afterY - k))
// const priceImpact = (beforePrice - afterPrice) / beforePrice * 100
return new bignumber_js_1.default(amountOutWithSlippage);
}
exports.getOutAmountStable = getOutAmountStable;
/* eslint-disable */
function addLiquidity(connection, wallet, poolInfo, fromCoinAccount, toCoinAccount, lpAccount, fromCoin, toCoin, fromAmount, toAmount, fixedCoin) {
return __awaiter(this, void 0, void 0, function* () {
if (!connection || !wallet)
throw new Error('Miss connection');
if (!poolInfo || !fromCoin || !toCoin) {
throw new Error('Miss pool infomations');
}
if (!fromCoinAccount || !toCoinAccount) {
throw new Error('Miss account infomations');
}
if (!fromAmount || !toAmount) {
throw new Error('Miss amount infomations');
}
const transaction = new web3_js_1.Transaction();
const signers = [];
const owner = wallet.publicKey;
const userAccounts = [new web3_js_1.PublicKey(fromCoinAccount), new web3_js_1.PublicKey(toCoinAccount)];
const userAmounts = [fromAmount, toAmount];
if (poolInfo.coin.mintAddress === toCoin.mintAddress && poolInfo.pc.mintAddress === fromCoin.mintAddress) {
userAccounts.reverse();
userAmounts.reverse();
}
const userCoinTokenAccount = userAccounts[0];
const userPcTokenAccount = userAccounts[1];
const coinAmount = layouts_1.getBigNumber(new safe_math_1.TokenAmount(userAmounts[0], poolInfo.coin.decimals, false).wei);
const pcAmount = layouts_1.getBigNumber(new safe_math_1.TokenAmount(userAmounts[1], poolInfo.pc.decimals, false).wei);
let wrappedCoinSolAccount;
if (poolInfo.coin.mintAddress === tokens_1.NATIVE_SOL.mintAddress) {
wrappedCoinSolAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedCoinSolAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, coinAmount + 1e7, transaction, signers);
}
let wrappedSolAccount;
if (poolInfo.pc.mintAddress === tokens_1.NATIVE_SOL.mintAddress) {
wrappedSolAccount = yield web3_1.createTokenAccountIfNotExist(connection, wrappedSolAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, pcAmount + 1e7, transaction, signers);
}
let userLpTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(lpAccount, owner, poolInfo.lp.mintAddress, transaction);
transaction.add([4, 5].includes(poolInfo.version)
? addLiquidityInstructionV4(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.lp.mintAddress), new web3_js_1.PublicKey(poolInfo.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfo.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfo.serumMarket), wrappedCoinSolAccount ? wrappedCoinSolAccount : userCoinTokenAccount, wrappedSolAccount ? wrappedSolAccount : userPcTokenAccount, userLpTokenAccount, owner, coinAmount, pcAmount, fixedCoin === poolInfo.coin.mintAddress ? 0 : 1)
: addLiquidityInstruction(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.ammQuantities), new web3_js_1.PublicKey(poolInfo.lp.mintAddress), new web3_js_1.PublicKey(poolInfo.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfo.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfo.serumMarket), wrappedCoinSolAccount ? wrappedCoinSolAccount : userCoinTokenAccount, wrappedSolAccount ? wrappedSolAccount : userPcTokenAccount, userLpTokenAccount, owner, coinAmount, pcAmount, fixedCoin === poolInfo.coin.mintAddress ? 0 : 1));
if (wrappedCoinSolAccount) {
transaction.add(token_instructions_1.closeAccount({
source: wrappedCoinSolAccount,
destination: owner,
owner: owner,
}));
}
if (wrappedSolAccount) {
transaction.add(token_instructions_1.closeAccount({
source: wrappedSolAccount,
destination: owner,
owner: owner,
}));
}
return yield web3_1.sendTransaction(connection, wallet, transaction, signers);
});
}
exports.addLiquidity = addLiquidity;
function removeLiquidity(connection, wallet, poolInfo, lpAccount, fromCoinAccount, toCoinAccount, amount) {
return __awaiter(this, void 0, void 0, function* () {
if (!connection || !wallet)
throw new Error('Miss connection');
if (!poolInfo)
throw new Error('Miss pool infomations');
if (!lpAccount)
throw new Error('Miss account infomations');
if (!amount)
throw new Error('Miss amount infomations');
const transaction = new web3_js_1.Transaction();
const signers = [];
const owner = wallet.publicKey;
const lpAmount = layouts_1.getBigNumber(new safe_math_1.TokenAmount(amount, poolInfo.lp.decimals, false).wei);
let needCloseFromTokenAccount = false;
let newFromTokenAccount;
if (poolInfo.coin.mintAddress === tokens_1.NATIVE_SOL.mintAddress) {
newFromTokenAccount = yield web3_1.createTokenAccountIfNotExist(connection, newFromTokenAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, null, transaction, signers);
needCloseFromTokenAccount = true;
}
else {
newFromTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(fromCoinAccount, owner, poolInfo.coin.mintAddress, transaction);
}
let needCloseToTokenAccount = false;
let newToTokenAccount;
if (poolInfo.pc.mintAddress === tokens_1.NATIVE_SOL.mintAddress) {
newToTokenAccount = yield web3_1.createTokenAccountIfNotExist(connection, newToTokenAccount, owner, tokens_1.TOKENS.WSOL.mintAddress, null, transaction, signers);
needCloseToTokenAccount = true;
}
else {
newToTokenAccount = yield web3_1.createAssociatedTokenAccountIfNotExist(toCoinAccount, owner, poolInfo.pc.mintAddress === tokens_1.NATIVE_SOL.mintAddress ? tokens_1.TOKENS.WSOL.mintAddress : poolInfo.pc.mintAddress, transaction);
}
transaction.add([4, 5].includes(poolInfo.version)
? removeLiquidityInstructionV4(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.lp.mintAddress), new web3_js_1.PublicKey(poolInfo.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfo.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfo.poolWithdrawQueue), new web3_js_1.PublicKey(poolInfo.poolTempLpTokenAccount), new web3_js_1.PublicKey(poolInfo.serumProgramId), new web3_js_1.PublicKey(poolInfo.serumMarket), new web3_js_1.PublicKey(poolInfo.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfo.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfo.serumVaultSigner), new web3_js_1.PublicKey(lpAccount), newFromTokenAccount, newToTokenAccount, owner, lpAmount)
: removeLiquidityInstruction(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.ammQuantities), new web3_js_1.PublicKey(poolInfo.lp.mintAddress), new web3_js_1.PublicKey(poolInfo.poolCoinTokenAccount), new web3_js_1.PublicKey(poolInfo.poolPcTokenAccount), new web3_js_1.PublicKey(poolInfo.poolWithdrawQueue), new web3_js_1.PublicKey(poolInfo.poolTempLpTokenAccount), new web3_js_1.PublicKey(poolInfo.serumProgramId), new web3_js_1.PublicKey(poolInfo.serumMarket), new web3_js_1.PublicKey(poolInfo.serumCoinVaultAccount), new web3_js_1.PublicKey(poolInfo.serumPcVaultAccount), new web3_js_1.PublicKey(poolInfo.serumVaultSigner), new web3_js_1.PublicKey(lpAccount), newFromTokenAccount, newToTokenAccount, owner, lpAmount));
if (needCloseFromTokenAccount) {
transaction.add(token_instructions_1.closeAccount({
source: newFromTokenAccount,
destination: owner,
owner: owner,
}));
}
if (needCloseToTokenAccount) {
transaction.add(token_instructions_1.closeAccount({
source: newToTokenAccount,
destination: owner,
owner: owner,
}));
}
return yield web3_1.sendTransaction(connection, wallet, transaction, signers);
});
}
exports.removeLiquidity = removeLiquidity;
function addLiquidityInstruction(programId,
// tokenProgramId: PublicKey,
// amm
ammId, ammAuthority, ammOpenOrders, ammQuantities, lpMintAddress, poolCoinTokenAccount, poolPcTokenAccount,
// serum
serumMarket,
// user
userCoinTokenAccount, userPcTokenAccount, userLpTokenAccount, userOwner, maxCoinAmount, maxPcAmount, fixedFromCoin) {
const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('maxCoinAmount'), buffer_layout_1.nu64('maxPcAmount'), buffer_layout_1.nu64('fixedFromCoin')]);
const keys = [
{ pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ammId, isSigner: false, isWritable: true },
{ pubkey: ammAuthority, isSigner: false, isWritable: false },
{ pubkey: ammOpenOrders, isSigner: false, isWritable: false },
{ pubkey: ammQuantities, isSigner: false, isWritable: true },
{ pubkey: lpMintAddress, isSigner: false, isWritable: true },
{ pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: serumMarket, isSigner: false, isWritable: false },
{ pubkey: userCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userOwner, isSigner: true, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({
instruction: 3,
maxCoinAmount,
maxPcAmount,
fixedFromCoin,
}, data);
return new web3_js_1.TransactionInstruction({
keys,
programId,
data,
});
}
exports.addLiquidityInstruction = addLiquidityInstruction;
function addLiquidityInstructionV4(programId,
// tokenProgramId: PublicKey,
// amm
ammId, ammAuthority, ammOpenOrders, ammTargetOrders, lpMintAddress, poolCoinTokenAccount, poolPcTokenAccount,
// serum
serumMarket,
// user
userCoinTokenAccount, userPcTokenAccount, userLpTokenAccount, userOwner, maxCoinAmount, maxPcAmount, fixedFromCoin) {
const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('maxCoinAmount'), buffer_layout_1.nu64('maxPcAmount'), buffer_layout_1.nu64('fixedFromCoin')]);
const keys = [
{ pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ammId, isSigner: false, isWritable: true },
{ pubkey: ammAuthority, isSigner: false, isWritable: false },
{ pubkey: ammOpenOrders, isSigner: false, isWritable: false },
{ pubkey: ammTargetOrders, isSigner: false, isWritable: true },
{ pubkey: lpMintAddress, isSigner: false, isWritable: true },
{ pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: serumMarket, isSigner: false, isWritable: false },
{ pubkey: userCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userOwner, isSigner: true, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({
instruction: 3,
maxCoinAmount,
maxPcAmount,
fixedFromCoin,
}, data);
return new web3_js_1.TransactionInstruction({
keys,
programId,
data,
});
}
exports.addLiquidityInstructionV4 = addLiquidityInstructionV4;
function removeLiquidityInstruction(programId,
// tokenProgramId: PublicKey,
// amm
ammId, ammAuthority, ammOpenOrders, ammQuantities, lpMintAddress, poolCoinTokenAccount, poolPcTokenAccount, poolWithdrawQueue, poolTempLpTokenAccount,
// serum
serumProgramId, serumMarket, serumCoinVaultAccount, serumPcVaultAccount, serumVaultSigner,
// user
userLpTokenAccount, userCoinTokenAccount, userPcTokenAccount, userOwner, amount) {
const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amount')]);
const keys = [
{ pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ammId, isSigner: false, isWritable: true },
{ pubkey: ammAuthority, isSigner: false, isWritable: false },
{ pubkey: ammOpenOrders, isSigner: false, isWritable: true },
{ pubkey: ammQuantities, isSigner: false, isWritable: true },
{ pubkey: lpMintAddress, isSigner: false, isWritable: true },
{ pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolWithdrawQueue, isSigner: false, isWritable: true },
{ pubkey: poolTempLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: serumProgramId, isSigner: false, isWritable: false },
{ pubkey: serumMarket, isSigner: false, isWritable: true },
{ pubkey: serumCoinVaultAccount, isSigner: false, isWritable: true },
{ pubkey: serumPcVaultAccount, isSigner: false, isWritable: true },
{ pubkey: serumVaultSigner, isSigner: false, isWritable: false },
{ pubkey: userLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userOwner, isSigner: true, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({
instruction: 4,
amount: amount,
}, data);
return new web3_js_1.TransactionInstruction({
keys,
programId,
data,
});
}
exports.removeLiquidityInstruction = removeLiquidityInstruction;
function removeLiquidityInstructionV4(programId,
// tokenProgramId: PublicKey,
// amm
ammId, ammAuthority, ammOpenOrders, ammTargetOrders, lpMintAddress, poolCoinTokenAccount, poolPcTokenAccount, poolWithdrawQueue, poolTempLpTokenAccount,
// serum
serumProgramId, serumMarket, serumCoinVaultAccount, serumPcVaultAccount, serumVaultSigner,
// user
userLpTokenAccount, userCoinTokenAccount, userPcTokenAccount, userOwner, amount) {
const dataLayout = buffer_layout_1.struct([buffer_layout_1.u8('instruction'), buffer_layout_1.nu64('amount')]);
const keys = [
{ pubkey: ids_1.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ 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: lpMintAddress, isSigner: false, isWritable: true },
{ pubkey: poolCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolWithdrawQueue, isSigner: false, isWritable: true },
{ pubkey: poolTempLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: serumProgramId, isSigner: false, isWritable: false },
{ pubkey: serumMarket, isSigner: false, isWritable: true },
{ pubkey: serumCoinVaultAccount, isSigner: false, isWritable: true },
{ pubkey: serumPcVaultAccount, isSigner: false, isWritable: true },
{ pubkey: serumVaultSigner, isSigner: false, isWritable: false },
{ pubkey: userLpTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userCoinTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userPcTokenAccount, isSigner: false, isWritable: true },
{ pubkey: userOwner, isSigner: true, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({
instruction: 4,
amount: amount,
}, data);
return new web3_js_1.TransactionInstruction({
keys,
programId,
data,
});
}
exports.removeLiquidityInstructionV4 = removeLiquidityInstructionV4;
exports.AMM_INFO_LAYOUT = buffer_layout_1.struct([
borsh_1.u64('status'),
borsh_1.u64('nonce'),
borsh_1.u64('orderNum'),
borsh_1.u64('depth'),
borsh_1.u64('coinDecimals'),
borsh_1.u64('pcDecimals'),
borsh_1.u64('state'),
borsh_1.u64('resetFlag'),
borsh_1.u64('fee'),
borsh_1.u64('minSize'),
borsh_1.u64('volMaxCutRatio'),
borsh_1.u64('pnlRatio'),
borsh_1.u64('amountWaveRatio'),
borsh_1.u64('coinLotSize'),
borsh_1.u64('pcLotSize'),
borsh_1.u64('minPriceMultiplier'),
borsh_1.u64('maxPriceMultiplier'),
borsh_1.u64('needTakePnlCoin'),
borsh_1.u64('needTakePnlPc'),
borsh_1.u64('totalPnlX'),
borsh_1.u64('totalPnlY'),
borsh_1.u64('systemDecimalsValue'),
borsh_1.publicKey('poolCoinTokenAccount'),
borsh_1.publicKey('poolPcTokenAccount'),
borsh_1.publicKey('coinMintAddress'),
borsh_1.publicKey('pcMintAddress'),
borsh_1.publicKey('lpMintAddress'),
borsh_1.publicKey('ammOpenOrders'),
borsh_1.publicKey('serumMarket'),
borsh_1.publicKey('serumProgramId'),
borsh_1.publicKey('ammTargetOrders'),
borsh_1.publicKey('ammQuantities'),
borsh_1.publicKey('poolWithdrawQueue'),
borsh_1.publicKey('poolTempLpTokenAccount'),
borsh_1.publicKey('ammOwner'),
borsh_1.publicKey('pnlOwner'),
]);
exports.AMM_INFO_LAYOUT_V3 = buffer_layout_1.struct([
borsh_1.u64('status'),
borsh_1.u64('nonce'),
borsh_1.u64('orderNum'),
borsh_1.u64('depth'),
borsh_1.u64('coinDecimals'),
borsh_1.u64('pcDecimals'),
borsh_1.u64('state'),
borsh_1.u64('resetFlag'),
borsh_1.u64('fee'),
borsh_1.u64('min_separate'),
borsh_1.u64('minSize'),
borsh_1.u64('volMaxCutRatio'),
borsh_1.u64('pnlRatio'),
borsh_1.u64('amountWaveRatio'),
borsh_1.u64('coinLotSize'),
borsh_1.u64('pcLotSize'),
borsh_1.u64('minPriceMultiplier'),
borsh_1.u64('maxPriceMultiplier'),
borsh_1.u64('needTakePnlCoin'),
borsh_1.u64('needTakePnlPc'),
borsh_1.u64('totalPnlX'),
borsh_1.u64('totalPnlY'),
borsh_1.u64('poolTotalDepositPc'),
borsh_1.u64('poolTotalDepositCoin'),
borsh_1.u64('systemDecimalsValue'),
borsh_1.publicKey('poolCoinTokenAccount'),
borsh_1.publicKey('poolPcTokenAccount'),
borsh_1.publicKey('coinMintAddress'),
borsh_1.publicKey('pcMintAddress'),
borsh_1.publicKey('lpMintAddress'),
borsh_1.publicKey('ammOpenOrders'),
borsh_1.publicKey('serumMarket'),
borsh_1.publicKey('serumProgramId'),
borsh_1.publicKey('ammTargetOrders'),
borsh_1.publicKey('ammQuantities'),
borsh_1.publicKey('poolWithdrawQueue'),
borsh_1.publicKey('poolTempLpTokenAccount'),
borsh_1.publicKey('ammOwner'),
borsh_1.publicKey('pnlOwner'),
borsh_1.publicKey('srmTokenAccount'),
]);
exports.AMM_INFO_LAYOUT_V4 = buffer_layout_1.struct([
borsh_1.u64('status'),
borsh_1.u64('nonce'),
borsh_1.u64('orderNum'),
borsh_1.u64('depth'),
borsh_1.u64('coinDecimals'),
borsh_1.u64('pcDecimals'),
borsh_1.u64('state'),
borsh_1.u64('resetFlag'),
borsh_1.u64('minSize'),
borsh_1.u64('volMaxCutRatio'),
borsh_1.u64('amountWaveRatio'),
borsh_1.u64('coinLotSize'),
borsh_1.u64('pcLotSize'),
borsh_1.u64('minPriceMultiplier'),
borsh_1.u64('maxPriceMultiplier'),
borsh_1.u64('systemDecimalsValue'),
// Fees
borsh_1.u64('minSeparateNumerator'),
borsh_1.u64('minSeparateDenominator'),
borsh_1.u64('tradeFeeNumerator'),
borsh_1.u64('tradeFeeDenominator'),
borsh_1.u64('pnlNumerator'),
borsh_1.u64('pnlDenominator'),
borsh_1.u64('swapFeeNumerator'),
borsh_1.u64('swapFeeDenominator'),
// OutPutData
borsh_1.u64('needTakePnlCoin'),
borsh_1.u64('needTakePnlPc'),
borsh_1.u64('totalPnlPc'),
borsh_1.u64('totalPnlCoin'),
borsh_1.u128('poolTotalDepositPc'),
borsh_1.u128('poolTotalDepositCoin'),
borsh_1.u128('swapCoinInAmount'),
borsh_1.u128('swapPcOutAmount'),
borsh_1.u64('swapCoin2PcFee'),
borsh_1.u128('swapPcInAmount'),
borsh_1.u128('swapCoinOutAmount'),
borsh_1.u64('swapPc2CoinFee'),
borsh_1.publicKey('poolCoinTokenAccount'),
borsh_1.publicKey('poolPcTokenAccount'),
borsh_1.publicKey('coinMintAddress'),
borsh_1.publicKey('pcMintAddress'),
borsh_1.publicKey('lpMintAddress'),
borsh_1.publicKey('ammOpenOrders'),
borsh_1.publicKey('serumMarket'),
borsh_1.publicKey('serumProgramId'),
borsh_1.publicKey('ammTargetOrders'),
borsh_1.publicKey('poolWithdrawQueue'),
borsh_1.publicKey('poolTempLpTokenAccount'),
borsh_1.publicKey('ammOwner'),
borsh_1.publicKey('pnlOwner'),
]);
exports.AMM_INFO_LAYOUT_STABLE = buffer_layout_1.struct([
borsh_1.u64('status'),
borsh_1.publicKey('own_address'),
borsh_1.u64('nonce'),
borsh_1.u64('orderNum'),
borsh_1.u64('depth'),
borsh_1.u64('coinDecimals'),
borsh_1.u64('pcDecimals'),
borsh_1.u64('state'),
borsh_1.u64('resetFlag'),
borsh_1.u64('minSize'),
borsh_1.u64('volMaxCutRatio'),
borsh_1.u64('amountWaveRatio'),
borsh_1.u64('coinLotSize'),
borsh_1.u64('pcLotSize'),
borsh_1.u64('minPriceMultiplier'),
borsh_1.u64('maxPriceMultiplier'),
borsh_1.u64('systemDecimalsValue'),
borsh_1.u64('ammMaxPrice'),
borsh_1.u64('ammMiddlePrice'),
borsh_1.u64('ammPriceMultiplier'),
// Fees
borsh_1.u64('minSeparateNumerator'),
borsh_1.u64('minSeparateDenominator'),
borsh_1.u64('tradeFeeNumerator'),
borsh_1.u64('tradeFeeDenominator'),
borsh_1.u64('pnlNumerator'),
borsh_1.u64('pnlDenominator'),
borsh_1.u64('swapFeeNumerator'),
borsh_1.u64('swapFeeDenominator'),
// OutPutData
borsh_1.u64('needTakePnlCoin'),
borsh_1.u64('needTakePnlPc'),
borsh_1.u64('totalPnlPc'),
borsh_1.u64('totalPnlCoin'),
borsh_1.u128('poolTotalDepositPc'),
borsh_1.u128('poolTotalDepositCoin'),
borsh_1.u128('swapCoinInAmount'),
borsh_1.u128('swapPcOutAmount'),
borsh_1.u128('swapPcInAmount'),
borsh_1.u128('swapCoinOutAmount'),
borsh_1.u64('swapPcFee'),
borsh_1.u64('swapCoinFee'),
borsh_1.publicKey('poolCoinTokenAccount'),
borsh_1.publicKey('poolPcTokenAccount'),
borsh_1.publicKey('coinMintAddress'),
borsh_1.publicKey('pcMintAddress'),
borsh_1.publicKey('lpMintAddress'),
borsh_1.publicKey('ammOpenOrders'),
borsh_1.publicKey('serumMarket'),
borsh_1.publicKey('serumProgramId'),
borsh_1.publicKey('ammTargetOrders'),
borsh_1.publicKey('poolWithdrawQueue'),
borsh_1.publicKey('poolTempLpTokenAccount'),
borsh_1.publicKey('ammOwner'),
borsh_1.publicKey('pnlOwner'),
borsh_1.u128('currentK'),
borsh_1.u128('padding1'),
borsh_1.publicKey('padding2'),
]);
function getLpMintInfo(conn, mintAddress, coin, pc) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let lpInfo = Object.values(tokens_1.LP_TOKENS).find((item) => item.mintAddress === mintAddress);
if (!lpInfo) {
const mintAll = yield web3_1.getMultipleAccounts(conn, [new web3_js_1.PublicKey(mintAddress)], web3_1.commitment);
if (mintAll !== null) {
const data = Buffer.from((_b = (_a = mintAll[0]) === null || _a === void 0 ? void 0 : _a.account.data) !== null && _b !== void 0 ? _b : '');
const mintLayoutData = layouts_1.MINT_LAYOUT.decode(data);
lpInfo = {
symbol: 'unknown',
name: 'unknown',
coin,
pc,
mintAddress: mintAddress,
decimals: mintLayoutData.decimals,
};
}
}
return lpInfo;
});
}
exports.getLpMintInfo = getLpMintInfo;
function getLpMintListDecimals(conn, mintAddressInfos) {
return __awaiter(this, void 0, void 0, function* () {
const reLpInfoDict = {};
const mintList = [];
mintAddressInfos.forEach((item) => {
let lpInfo = Object.values(tokens_1.LP_TOKENS).find((itemLpToken) => itemLpToken.mintAddress === item);
if (!lpInfo) {
mintList.push(new web3_js_1.PublicKey(item));
lpInfo = {
decimals: null,
};
}
reLpInfoDict[item] = lpInfo.decimals;
});
const mintAll = yield web3_1.getMultipleAccounts(conn, mintList, web3_1.commitment);
for (let mintIndex = 0; mintIndex < mintAll.length; mintIndex += 1) {
const itemMint = mintAll[mintIndex];
if (itemMint) {
const mintLayoutData = layouts_1.MINT_LAYOUT.decode(Buffer.from(itemMint.account.data));
reLpInfoDict[mintList[mintIndex].toString()] = mintLayoutData.decimals;
}
}
const reInfo = {};
for (const key of Object.keys(reLpInfoDict)) {
if (reLpInfoDict[key] !== null) {
reInfo[key] = reLpInfoDict[key];
}
}
return reInfo;
});
}
exports.getLpMintListDecimals = getLpMintListDecimals;
function getLiquidityInfoSimilar(ammIdOrMarket, from, to) {
// const fromCoin = from === NATIVE_SOL.mintAddress ? TOKENS.WSOL.mintAddress : from
// const toCoin = to === NATIVE_SOL.mintAddress ? TOKENS.WSOL.mintAddress : to
const fromCoin = from === tokens_1.TOKENS.WSOL.mintAddress ? tokens_1.NATIVE_SOL.mintAddress : from;
const toCoin = to === tokens_1.TOKENS.WSOL.mintAddress ? tokens_1.NATIVE_SOL.mintAddress : to;
const knownLiquidity = pools_1.LIQUIDITY_POOLS.find((item) => {
if (fromCoin !== undefined && toCoin != undefined && fromCoin === toCoin) {
return false;
}
if (ammIdOrMarket !== undefined && !(item.ammId === ammIdOrMarket || item.serumMarket === ammIdOrMarket)) {
return false;
}
if (fromCoin && item.pc.mintAddress !== fromCoin && item.coin.mintAddress !== fromCoin) {
return false;
}
if (toCoin && item.pc.mintAddress !== toCoin && item.coin.mintAddress !== toCoin) {
return false;
}
if (ammIdOrMarket || (fromCoin && toCoin)) {
return true;
}
return false;
});
return knownLiquidity;
}
exports.getLiquidityInfoSimilar = getLiquidityInfoSimilar;
function getLiquidityInfo(from, to) {
const fromCoin = from === tokens_1.TOKENS.WSOL.mintAddress ? tokens_1.NATIVE_SOL.mintAddress : from;
const toCoin = to === tokens_1.TOKENS.WSOL.mintAddress ? tokens_1.NATIVE_SOL.mintAddress : to;
return pools_1.LIQUIDITY_POOLS.filter((item) => item.version === 4 &&
((item.coin.mintAddress === fromCoin && item.pc.mintAddress === toCoin) ||
(item.coin.mintAddress === toCoin && item.pc.mintAddress === fromCoin)));
}
exports.getLiquidityInfo = getLiquidityInfo;
function requestInfos(conn) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// commit('setLoading', true)
let ammAll = [];
let marketAll = [];
yield Promise.all([
yield (() => __awaiter(this, void 0, void 0, function* () {
ammAll = yield web3_1.getFilteredProgramAccounts(conn, new web3_js_1.PublicKey(ids_1.LIQUIDITY_POOL_PROGRAM_ID_V4), [
{
dataSize: exports.AMM_INFO_LAYOUT_V4.span,
},
]);
}))(),
yield (() => __awaiter(this, void 0, void 0, function* () {
marketAll = yield web3_1.getFilteredProgramAccounts(conn, new web3_js_1.PublicKey(ids_1.SERUM_PROGRAM_ID_V3), [
{
dataSize: serum_1.MARKET_STATE_LAYOUT_V2.span,
},
]);
}))(),
]);
const marketToLayout = {};
marketAll.forEach((item) => {
marketToLayout[item.publicKey.toString()] = serum_1.MARKET_STATE_LAYOUT_V2.decode(item.accountInfo.data);
});
const lpMintAddressList = [];
ammAll.forEach((item) => {
const ammLayout = exports.AMM_INFO_LAYOUT_V4.decode(Buffer.from(item.accountInfo.data));
if (ammLayout.pcMintAddress.toString() === ammLayout.serumMarket.toString() ||
ammLayout.lpMintAddress.toString() === '11111111111111111111111111111111') {
return;
}
lpMintAddressList.push(ammLayout.lpMintAddress.toString());
});
const lpMintListDecimls = yield getLpMintListDecimals(conn, lpMintAddressList);
for (let indexAmmInfo = 0; indexAmmInfo < ammAll.length; indexAmmInfo += 1) {
const ammInfo = exports.AMM_INFO_LAYOUT_V4.decode(Buffer.from(ammAll[indexAmmInfo].accountInfo.data));
if (!Object.keys(lpMintListDecimls).includes(ammInfo.lpMintAddress.toString()) ||
ammInfo.pcMintAddress.toString() === ammInfo.serumMarket.toString() ||
ammInfo.lpMintAddress.toString() === '11111111111111111111111111111111' ||
!Object.keys(marketToLayout).includes(ammInfo.serumMarket.toString())) {
continue;
}
const fromCoin = ammInfo.coinMintAddress.toString() === tokens_1.TOKENS.WSOL.mintAddress
? tokens_1.NATIVE_SOL.mintAddress
: ammInfo.coinMintAddress.toString();
const toCoin = ammInfo.pcMintAddress.toString() === tokens_1.TOKENS.WSOL.mintAddress
? tokens_1.NATIVE_SOL.mintAddress
: ammInfo.pcMintAddress.toString();
let coin = Object.values(tokens_1.TOKENS).find((item) => item.mintAddress === fromCoin);
if (!coin && fromCoin !== tokens_1.NATIVE_SOL.mintAddress) {
tokens_1.TOKENS[`unknow-${ammInfo.coinMintAddress.toString()}`] = {
symbol: 'unknown',
name: 'unknown',
mintAddress: ammInfo.coinMintAddress.toString(),
decimals: layouts_1.getBigNumber(ammInfo.coinDecimals),
cache: true,
tags: [],
};
coin = tokens_1.TOKENS[`unknow-${ammInfo.coinMintAddress.toString()}`];
}
else if (fromCoin === tokens_1.NATIVE_SOL.mintAddress) {
coin = tokens_1.NATIVE_SOL;
}
if (!coin.tags.includes('unofficial')) {
coin.tags.push('unofficial');
}
let pc = Object.values(tokens_1.TOKENS).find((item) => item.mintAddress === toCoin);
if (!pc && toCoin !== tokens_1.NATIVE_SOL.mintAddress) {
tokens_1.TOKENS[`unknow-${ammInfo.pcMintAddress.toString()}`] = {
symbol: 'unknown',
name: 'unknown',
mintAddress: ammInfo.pcMintAddress.toString(),
decimals: layouts_1.getBigNumber(ammInfo.pcDecimals),
cache: true,
tags: [],
};
pc = tokens_1.TOKENS[`unknow-${ammInfo.pcMintAddress.toString()}`];
}
else if (toCoin === tokens_1.NATIVE_SOL.mintAddress) {
pc = tokens_1.NATIVE_SOL;
}
if (!pc.tags.includes('unofficial')) {
pc.tags.push('unofficial');
}
if (coin.mintAddress === tokens_1.TOKENS.WSOL.mintAddress) {
coin.symbol = 'SOL';
coin.name = 'SOL';
coin.mintAddress = '11111111111111111111111111111111';
}
if (pc.mintAddress === tokens_1.TOKENS.WSOL.mintAddress) {
pc.symbol = 'SOL';
pc.name = 'SOL';
pc.mintAddress = '11111111111111111111111111111111';
}
const lp = (_a = Object.values(tokens_1.LP_TOKENS).find((item) => item.mintAddress === ammInfo.lpMintAddress)) !== null && _a !== void 0 ? _a : {
symbol: `${coin.symbol}-${pc.symbol}`,
name: `${coin.symbol}-${pc.symbol}`,
coin,
pc,
mintAddress: ammInfo.lpMintAddress.toString(),
decimals: lpMintListDecimls[ammInfo.lpMintAddress],
};
const { publicKey } = yield web3_1.createAmmAuthority(new web3_js_1.PublicKey(ids_1.LIQUIDITY_POOL_PROGRAM_ID_V4));
const market = marketToLayout[ammInfo.serumMarket];
const serumVaultSigner = yield web3_js_1.PublicKey.createProgramAddress([ammInfo.serumMarket.toBuffer(), market.vaultSignerNonce.toArrayLike(Buffer, 'le', 8)], new web3_js_1.PublicKey(ids_1.SERUM_PROGRAM_ID_V3));
const itemLiquidity = {
name: `${coin.symbol}-${pc.symbol}`,
coin,
pc,
lp,
version: 4,
programId: ids_1.LIQUIDITY_POOL_PROGRAM_ID_V4,
ammId: ammAll[indexAmmInfo].publicKey.toString(),
ammAuthority: publicKey.toString(),
ammOpenOrders: ammInfo.ammOpenOrders.toString(),
ammTargetOrders: ammInfo.ammTargetOrders.toString(),
ammQuantities: tokens_1.NATIVE_SOL.mintAddress,
poolCoinTokenAccount: ammInfo.poolCoinTokenAccount.toString(),
poolPcTokenAccount: ammInfo.poolPcTokenAccount.toString(),
poolWithdrawQueue: ammInfo.poolWithdrawQueue.toString(),
poolTempLpTokenAccount: ammInfo.poolTempLpTokenAccount.toString(),
serumProgramId: ids_1.SERUM_PROGRAM_ID_V3,
serumMarket: ammInfo.serumMarket.toString(),
serumBids: market.bids.toString(),
serumAsks: market.asks.toString(),
serumEventQueue: market.eventQueue.toString(),
serumCoinVaultAccount: market.baseVault.toString(),
serumPcVaultAccount: market.quoteVault.toString(),
serumVaultSigner: serumVaultSigner.toString(),
official: false,
};
if (!pools_1.LIQUIDITY_POOLS.find((item) => item.ammId === itemLiquidity.ammId)) {
pools_1.LIQUIDITY_POOLS.push(itemLiquidity);
}
else {
for (let itemIndex = 0; itemIndex < pools_1.LIQUIDITY_POOLS.length; itemIndex += 1) {
if (pools_1.LIQUIDITY_POOLS[itemIndex].ammId === itemLiquidity.ammId &&
pools_1.LIQUIDITY_POOLS[itemIndex].name !== itemLiquidity.name &&
!pools_1.LIQUIDITY_POOLS[itemIndex].official) {
pools_1.LIQUIDITY_POOLS[itemIndex] = itemLiquidity;
}
}
}
}
const liquidityPools = {};
const publicKeys = [];
pools_1.LIQUIDITY_POOLS.forEach((pool) => {
const { poolCoinTokenAccount, poolPcTokenAccount, ammOpenOrders, ammId, coin, pc, lp } = pool;
publicKeys.push(new web3_js_1.PublicKey(poolCoinTokenAccount), new web3_js_1.PublicKey(poolPcTokenAccount), new web3_js_1.PublicKey(ammOpenOrders), new web3_js_1.PublicKey(ammId), new web3_js_1.PublicKey(lp.mintAddress));
const poolInfo = lodash_1.cloneDeep(pool);
poolInfo.coin.balance = new safe_math_1.TokenAmount(0, coin.decimals);
poolInfo.pc.balance = new safe_math_1.TokenAmount(0, pc.decimals);
liquidityPools[lp.mintAddress] = poolInfo;
});
const multipleInfo = yield web3_1.getMultipleAccounts(conn, publicKeys, web3_1.commitment);
multipleInfo.forEach((info) => {
if (info) {
const address = info.publicKey.toBase58();
const data = Buffer.from(info.account.data);
const { key, lpMintAddress, version } = pools_1.getAddressForWhat(address);
if (key && lpMintAddress) {
const poolInfo = liquidityPools[lpMintAddress];
switch (key) {
case 'poolCoinTokenAccount': {
const parsed = layouts_1.ACCOUNT_LAYOUT.decode(data);
// quick fix: Number can only safely store up to 53 bits
poolInfo.coin.balance.wei = poolInfo.coin.balance.wei.plus(layouts_1.getBigNumber(parsed.amount));
break;
}
case 'poolPcTokenAccount': {
const parsed = layouts_1.ACCOUNT_LAYOUT.decode(data);
poolInfo.pc.balance.wei = poolInfo.pc.balance.wei.plus(layouts_1.getBigNumber(parsed.amount));
break;
}
case 'ammOpenOrders': {
const OPEN_ORDERS_LAYOUT = serum_1.OpenOrders.getLayout(new web3_js_1.PublicKey(poolInfo.serumProgramId));
const parsed = OPEN_ORDERS_LAYOUT.decode(data);
const { baseTokenTotal, quoteTokenTotal } = parsed;
poolInfo.coin.balance.wei = poolInfo.coin.balance.wei.plus(layouts_1.getBigNumber(baseTokenTotal));
poolInfo.pc.balance.wei = poolInfo.pc.balance.wei.plus(layouts_1.getBigNumber(quoteTokenTotal));
break;
}
case 'ammId': {
let parsed;
if (version === 2) {
parsed = exports.AMM_INFO_LAYOUT.decode(data);
}
else if (version === 3) {
parsed = exports.AMM_INFO_LAYOUT_V3.decode(data);
}
else {
if (version === 5) {
parsed = exports.AMM_INFO_LAYOUT_STABLE.decode(data);
poolInfo.currentK = layouts_1.getBigNumber(parsed.currentK);
}
else
parsed = exports.AMM_INFO_LAYOUT_V4.decode(data);
const { swapFeeNumerator, swapFeeDenominator } = parsed;
poolInfo.fees = {
swapFeeNumerator: layouts_1.getBigNumber(swapFeeNumerator),
swapFeeDenominator: layouts_1.getBigNumber(swapFeeDenominator),
};
}
const { status, needTakePnlCoin, needTakePnlPc } = parsed;
poolInfo.status = layouts_1.getBigNumber(status);
poolInfo.coin.balance.wei = poolInfo.coin.balance.wei.minus(layouts_1.getBigNumber(needTakePnlCoin));
poolInfo.pc.balance.wei = poolInfo.pc.balance.wei.minus(layouts_1.getBigNumber(needTakePnlPc));
break;
}
// getLpSupply
case 'lpMintAddress': {
const parsed = layouts_1.MINT_LAYOUT.decode(data);
poolInfo.lp.totalSupply = new safe_math_1.TokenAmount(layouts_1.getBigNumber(parsed.supply), poolInfo.lp.decimals);
break;
}
}
}
}
});
return liquidityPools;
});
}
exports.requestInfos = requestInfos;