UNPKG

test-raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

1 lines 506 kB
{"version":3,"sources":["../../../../src/raydium/clmm/utils/pool.ts","../../../../src/raydium/clmm/utils/constants.ts","../../../../src/raydium/clmm/utils/math.ts","../../../../node_modules/decimal.js/decimal.mjs","../../../../src/common/logger.ts","../../../../src/common/utility.ts","../../../../src/module/amount.ts","../../../../src/common/bignumber.ts","../../../../src/module/token.ts","../../../../src/common/pubKey.ts","../../../../src/raydium/token/constant.ts","../../../../src/module/fraction.ts","../../../../src/module/formatter.ts","../../../../src/module/price.ts","../../../../src/module/currency.ts","../../../../src/module/percent.ts","../../../../src/common/txTool/txTool.ts","../../../../src/common/txTool/txUtils.ts","../../../../src/common/txTool/lookupTable.ts","../../../../src/common/accountInfo.ts","../../../../src/common/lodash.ts","../../../../src/common/programId.ts","../../../../src/common/pda.ts","../../../../src/common/transfer.ts","../../../../src/raydium/clmm/utils/util.ts","../../../../src/raydium/clmm/utils/pda.ts","../../../../src/raydium/clmm/utils/tick.ts","../../../../src/marshmallow/index.ts","../../../../src/marshmallow/buffer-layout.ts","../../../../src/raydium/clmm/utils/tickarrayBitmap.ts","../../../../src/raydium/clmm/layout.ts","../../../../src/raydium/clmm/utils/tickQuery.ts","../../../../src/raydium/clmm/utils/position.ts"],"sourcesContent":["import { PublicKey, Connection, EpochInfo } from \"@solana/web3.js\";\nimport BN from \"bn.js\";\n\nimport {\n ClmmPoolInfo,\n ClmmPoolRewardInfo,\n ClmmPoolRewardLayoutInfo,\n ReturnTypeGetLiquidityAmountOut,\n TickArrayBitmapExtensionType,\n ReturnTypeFetchExBitmaps,\n ReturnTypeFetchMultiplePoolTickArrays,\n SDKParsedConcentratedInfo,\n ReturnTypeComputeAmountOut,\n ReturnTypeComputeAmountOutFormat,\n} from \"../type\";\n\nimport { ApiV3PoolInfoConcentratedItem } from \"@/api/type\";\n\nimport { ReturnTypeFetchMultipleMintInfos } from \"@/raydium/type\";\nimport { NEGATIVE_ONE, Q64, ZERO, MAX_TICK, MIN_TICK, MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64 } from \"./constants\";\nimport { MathUtil, SwapMath, SqrtPriceMath, LiquidityMath } from \"./math\";\nimport { getPdaTickArrayAddress, getPdaPersonalPositionAddress } from \"./pda\";\nimport { TickArray, TickUtils, TICK_ARRAY_BITMAP_SIZE, Tick } from \"./tick\";\nimport { TickArrayBitmap, TickArrayBitmapExtensionUtils } from \"./tickarrayBitmap\";\nimport { TickQuery } from \"./tickQuery\";\nimport { TickArrayBitmapExtensionLayout, PositionInfoLayout, TickArrayLayout } from \"../layout\";\nimport {\n getMultipleAccountsInfo,\n getMultipleAccountsInfoWithCustomFlags,\n getTransferAmountFee,\n getTransferAmountFeeV2,\n minExpirationTime,\n WSOLMint,\n SOLMint,\n solToWSol,\n} from \"../../../common\";\nimport { SOL_INFO } from \"../../token/constant\";\nimport { TokenAccountRaw } from \"../../account/types\";\nimport { Price, Percent, TokenAmount, Token } from \"../../../module\";\nimport { PositionUtils } from \"./position\";\nimport Decimal from \"decimal.js\";\n\nexport class PoolUtils {\n public static getOutputAmountAndRemainAccounts(\n poolInfo: ClmmPoolInfo,\n tickArrayCache: { [key: string]: TickArray },\n inputTokenMint: PublicKey,\n inputAmount: BN,\n sqrtPriceLimitX64?: BN,\n ): {\n expectedAmountOut: BN;\n remainingAccounts: PublicKey[];\n executionPrice: BN;\n feeAmount: BN;\n } {\n const zeroForOne = inputTokenMint.equals(poolInfo.mintA.mint);\n\n const allNeededAccounts: PublicKey[] = [];\n const {\n isExist,\n startIndex: firstTickArrayStartIndex,\n nextAccountMeta,\n } = this.getFirstInitializedTickArray(poolInfo, zeroForOne);\n if (!isExist || firstTickArrayStartIndex === undefined || !nextAccountMeta) throw new Error(\"Invalid tick array\");\n\n // try {\n // const preTick = this.preInitializedTickArrayStartIndex(poolInfo, !zeroForOne)\n // if (preTick.isExist) {\n // const { publicKey: address } = getPdaTickArrayAddress(\n // poolInfo.programId,\n // poolInfo.id,\n // preTick.nextStartIndex\n // );\n // allNeededAccounts.push(address)\n // }\n // } catch (e) { /* empty */ }\n\n allNeededAccounts.push(nextAccountMeta);\n const {\n amountCalculated: outputAmount,\n accounts: reaminAccounts,\n sqrtPriceX64: executionPrice,\n feeAmount,\n } = SwapMath.swapCompute(\n poolInfo.programId,\n poolInfo.id,\n tickArrayCache,\n poolInfo.tickArrayBitmap,\n poolInfo.exBitmapInfo,\n zeroForOne,\n poolInfo.ammConfig.tradeFeeRate,\n poolInfo.liquidity,\n poolInfo.tickCurrent,\n poolInfo.tickSpacing,\n poolInfo.sqrtPriceX64,\n inputAmount,\n firstTickArrayStartIndex,\n sqrtPriceLimitX64,\n );\n allNeededAccounts.push(...reaminAccounts);\n return {\n expectedAmountOut: outputAmount.mul(NEGATIVE_ONE),\n remainingAccounts: allNeededAccounts,\n executionPrice,\n feeAmount,\n };\n }\n\n public static getInputAmountAndRemainAccounts(\n poolInfo: ClmmPoolInfo,\n tickArrayCache: { [key: string]: TickArray },\n outputTokenMint: PublicKey,\n outputAmount: BN,\n sqrtPriceLimitX64?: BN,\n ): { expectedAmountIn: BN; remainingAccounts: PublicKey[]; executionPrice: BN; feeAmount: BN } {\n const zeroForOne = outputTokenMint.equals(poolInfo.mintB.mint);\n\n const allNeededAccounts: PublicKey[] = [];\n const {\n isExist,\n startIndex: firstTickArrayStartIndex,\n nextAccountMeta,\n } = this.getFirstInitializedTickArray(poolInfo, zeroForOne);\n if (!isExist || firstTickArrayStartIndex === undefined || !nextAccountMeta) throw new Error(\"Invalid tick array\");\n\n try {\n const preTick = this.preInitializedTickArrayStartIndex(poolInfo, zeroForOne);\n if (preTick.isExist) {\n const { publicKey: address } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.id, preTick.nextStartIndex);\n allNeededAccounts.push(address);\n }\n } catch (e) {\n /* empty */\n }\n\n allNeededAccounts.push(nextAccountMeta);\n const {\n amountCalculated: inputAmount,\n accounts: reaminAccounts,\n sqrtPriceX64: executionPrice,\n feeAmount,\n } = SwapMath.swapCompute(\n poolInfo.programId,\n poolInfo.id,\n tickArrayCache,\n poolInfo.tickArrayBitmap,\n poolInfo.exBitmapInfo,\n zeroForOne,\n poolInfo.ammConfig.tradeFeeRate,\n poolInfo.liquidity,\n poolInfo.tickCurrent,\n poolInfo.tickSpacing,\n poolInfo.sqrtPriceX64,\n outputAmount.mul(NEGATIVE_ONE),\n firstTickArrayStartIndex,\n sqrtPriceLimitX64,\n );\n allNeededAccounts.push(...reaminAccounts);\n return { expectedAmountIn: inputAmount, remainingAccounts: allNeededAccounts, executionPrice, feeAmount };\n }\n\n public static getFirstInitializedTickArray(\n poolInfo: ClmmPoolInfo,\n zeroForOne: boolean,\n ):\n | { isExist: true; startIndex: number; nextAccountMeta: PublicKey }\n | { isExist: false; startIndex: undefined; nextAccountMeta: undefined } {\n const { isInitialized, startIndex } = PoolUtils.isOverflowDefaultTickarrayBitmap(poolInfo.tickSpacing, [\n poolInfo.tickCurrent,\n ])\n ? TickArrayBitmapExtensionUtils.checkTickArrayIsInit(\n TickQuery.getArrayStartIndex(poolInfo.tickCurrent, poolInfo.tickSpacing),\n poolInfo.tickSpacing,\n poolInfo.exBitmapInfo,\n )\n : TickUtils.checkTickArrayIsInitialized(\n TickUtils.mergeTickArrayBitmap(poolInfo.tickArrayBitmap),\n poolInfo.tickCurrent,\n poolInfo.tickSpacing,\n );\n\n if (isInitialized) {\n const { publicKey: address } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.id, startIndex);\n return {\n isExist: true,\n startIndex,\n nextAccountMeta: address,\n };\n }\n const { isExist, nextStartIndex } = this.nextInitializedTickArrayStartIndex(\n poolInfo,\n TickQuery.getArrayStartIndex(poolInfo.tickCurrent, poolInfo.tickSpacing),\n zeroForOne,\n );\n if (isExist) {\n const { publicKey: address } = getPdaTickArrayAddress(poolInfo.programId, poolInfo.id, nextStartIndex);\n return {\n isExist: true,\n startIndex: nextStartIndex,\n nextAccountMeta: address,\n };\n }\n return { isExist: false, nextAccountMeta: undefined, startIndex: undefined };\n }\n\n public static preInitializedTickArrayStartIndex(\n poolInfo: ClmmPoolInfo,\n zeroForOne: boolean,\n ): { isExist: boolean; nextStartIndex: number } {\n const currentOffset = Math.floor(poolInfo.tickCurrent / TickQuery.tickCount(poolInfo.tickSpacing));\n\n const result: number[] = !zeroForOne\n ? TickUtils.searchLowBitFromStart(\n poolInfo.tickArrayBitmap,\n poolInfo.exBitmapInfo,\n currentOffset - 1,\n 1,\n poolInfo.tickSpacing,\n )\n : TickUtils.searchHightBitFromStart(\n poolInfo.tickArrayBitmap,\n poolInfo.exBitmapInfo,\n currentOffset + 1,\n 1,\n poolInfo.tickSpacing,\n );\n\n return result.length > 0 ? { isExist: true, nextStartIndex: result[0] } : { isExist: false, nextStartIndex: 0 };\n }\n\n public static nextInitializedTickArrayStartIndex(\n poolInfo:\n | {\n tickCurrent: number;\n tickSpacing: number;\n tickArrayBitmap: BN[];\n exBitmapInfo: TickArrayBitmapExtensionType;\n }\n | ClmmPoolInfo,\n lastTickArrayStartIndex: number,\n zeroForOne: boolean,\n ): { isExist: boolean; nextStartIndex: number } {\n lastTickArrayStartIndex = TickQuery.getArrayStartIndex(poolInfo.tickCurrent, poolInfo.tickSpacing);\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { isInit: startIsInit, tickIndex: startIndex } = TickArrayBitmap.nextInitializedTickArrayStartIndex(\n TickUtils.mergeTickArrayBitmap(poolInfo.tickArrayBitmap),\n lastTickArrayStartIndex,\n poolInfo.tickSpacing,\n zeroForOne,\n );\n if (startIsInit) {\n return { isExist: true, nextStartIndex: startIndex };\n }\n lastTickArrayStartIndex = startIndex;\n\n const { isInit, tickIndex } = TickArrayBitmapExtensionUtils.nextInitializedTickArrayFromOneBitmap(\n lastTickArrayStartIndex,\n poolInfo.tickSpacing,\n zeroForOne,\n poolInfo.exBitmapInfo,\n );\n if (isInit) return { isExist: true, nextStartIndex: tickIndex };\n\n lastTickArrayStartIndex = tickIndex;\n\n if (lastTickArrayStartIndex < MIN_TICK || lastTickArrayStartIndex > MAX_TICK)\n return { isExist: false, nextStartIndex: 0 };\n }\n\n // const tickArrayBitmap = TickUtils.mergeTickArrayBitmap(\n // poolInfo.tickArrayBitmap\n // );\n // const currentOffset = TickUtils.getTickArrayOffsetInBitmapByTick(\n // poolInfo.tickCurrent,\n // poolInfo.tickSpacing\n // );\n // const result: number[] = zeroForOne ? TickUtils.searchLowBitFromStart(\n // tickArrayBitmap,\n // currentOffset - 1,\n // 0,\n // 1,\n // poolInfo.tickSpacing\n // ) : TickUtils.searchHightBitFromStart(\n // tickArrayBitmap,\n // currentOffset,\n // 1024,\n // 1,\n // poolInfo.tickSpacing\n // );\n\n // return result.length > 0 ? { isExist: true, nextStartIndex: result[0] } : { isExist: false, nextStartIndex: 0 }\n }\n\n public static async updatePoolRewardInfos({\n connection,\n apiPoolInfo,\n chainTime,\n poolLiquidity,\n rewardInfos,\n }: {\n connection: Connection;\n apiPoolInfo: ApiV3PoolInfoConcentratedItem;\n chainTime: number;\n poolLiquidity: BN;\n rewardInfos: ClmmPoolRewardLayoutInfo[];\n }): Promise<ClmmPoolRewardInfo[]> {\n const nRewardInfo: ClmmPoolRewardInfo[] = [];\n for (let i = 0; i < rewardInfos.length; i++) {\n const _itemReward = rewardInfos[i];\n const apiRewardProgram =\n apiPoolInfo.rewardDefaultInfos[i]?.mint.programId ??\n (await connection.getAccountInfo(_itemReward.tokenMint))?.owner;\n if (apiRewardProgram === undefined) throw Error(\"get new reward mint info error\");\n\n const itemReward: ClmmPoolRewardInfo = {\n ..._itemReward,\n perSecond: MathUtil.x64ToDecimal(_itemReward.emissionsPerSecondX64),\n remainingRewards: undefined,\n tokenProgramId: new PublicKey(apiRewardProgram),\n };\n\n if (itemReward.tokenMint.equals(PublicKey.default)) continue;\n if (chainTime <= itemReward.openTime.toNumber() || poolLiquidity.eq(ZERO)) {\n nRewardInfo.push(itemReward);\n continue;\n }\n\n const latestUpdateTime = new BN(Math.min(itemReward.endTime.toNumber(), chainTime));\n const timeDelta = latestUpdateTime.sub(itemReward.lastUpdateTime);\n const rewardGrowthDeltaX64 = MathUtil.mulDivFloor(timeDelta, itemReward.emissionsPerSecondX64, poolLiquidity);\n const rewardGrowthGlobalX64 = itemReward.rewardGrowthGlobalX64.add(rewardGrowthDeltaX64);\n const rewardEmissionedDelta = MathUtil.mulDivFloor(timeDelta, itemReward.emissionsPerSecondX64, Q64);\n const rewardTotalEmissioned = itemReward.rewardTotalEmissioned.add(rewardEmissionedDelta);\n nRewardInfo.push({\n ...itemReward,\n rewardGrowthGlobalX64,\n rewardTotalEmissioned,\n lastUpdateTime: latestUpdateTime,\n });\n }\n return nRewardInfo;\n }\n\n public static isOverflowDefaultTickarrayBitmap(tickSpacing: number, tickarrayStartIndexs: number[]): boolean {\n const { maxTickBoundary, minTickBoundary } = this.tickRange(tickSpacing);\n\n for (const tickIndex of tickarrayStartIndexs) {\n const tickarrayStartIndex = TickUtils.getTickArrayStartIndexByTick(tickIndex, tickSpacing);\n\n if (tickarrayStartIndex >= maxTickBoundary || tickarrayStartIndex < minTickBoundary) {\n return true;\n }\n }\n\n return false;\n }\n\n public static tickRange(tickSpacing: number): {\n maxTickBoundary: number;\n minTickBoundary: number;\n } {\n let maxTickBoundary = TickArrayBitmap.maxTickInTickarrayBitmap(tickSpacing);\n let minTickBoundary = -maxTickBoundary;\n\n if (maxTickBoundary > MAX_TICK) {\n maxTickBoundary = MAX_TICK;\n }\n if (minTickBoundary < MIN_TICK) {\n minTickBoundary = MIN_TICK;\n }\n return { maxTickBoundary, minTickBoundary };\n }\n\n public static get_tick_array_offset(tickarrayStartIndex: number, tickSpacing: number): number {\n if (!TickQuery.checkIsValidStartIndex(tickarrayStartIndex, tickSpacing)) {\n throw new Error(\"No enough initialized tickArray\");\n }\n\n return (tickarrayStartIndex / TickQuery.tickCount(tickSpacing)) * TICK_ARRAY_BITMAP_SIZE;\n }\n\n static async fetchExBitmaps({\n connection,\n exBitmapAddress,\n batchRequest,\n }: {\n connection: Connection;\n exBitmapAddress: PublicKey[];\n batchRequest: boolean;\n }): Promise<ReturnTypeFetchExBitmaps> {\n const fetchedBitmapAccount = await getMultipleAccountsInfoWithCustomFlags(\n connection,\n exBitmapAddress.map((i) => ({ pubkey: i })),\n { batchRequest },\n );\n\n const returnTypeFetchExBitmaps: ReturnTypeFetchExBitmaps = {};\n for (const item of fetchedBitmapAccount) {\n if (item.accountInfo === null) continue;\n\n returnTypeFetchExBitmaps[item.pubkey.toString()] = TickArrayBitmapExtensionLayout.decode(item.accountInfo.data);\n }\n return returnTypeFetchExBitmaps;\n }\n\n // deprecated, new api doesn't need\n // static async fetchMultiplePoolInfos({\n // connection,\n // poolKeys,\n // ownerInfo,\n // chainTime,\n // batchRequest = false,\n // updateOwnerRewardAndFee = true,\n // }: {\n // connection: Connection;\n // poolKeys: ApiV3PoolInfoConcentratedItem[];\n // ownerInfo?: { wallet: PublicKey; tokenAccounts: TokenAccountRaw[] };\n // chainTime: number;\n // batchRequest?: boolean;\n // updateOwnerRewardAndFee?: boolean;\n // }): Promise<ReturnTypeFetchMultiplePoolInfos> {\n // const poolAccountInfos = await getMultipleAccountsInfo(\n // connection,\n // poolKeys.map((i) => new PublicKey(i.id)),\n // { batchRequest },\n // );\n // const exBitmapAddress: { [poolId: string]: PublicKey } = {};\n // for (let index = 0; index < poolKeys.length; index++) {\n // const apiPoolInfo = poolKeys[index];\n // const accountInfo = poolAccountInfos[index];\n\n // if (accountInfo === null) continue;\n // exBitmapAddress[apiPoolInfo.id] = getPdaExBitmapAccount(\n // accountInfo.owner,\n // new PublicKey(apiPoolInfo.id),\n // ).publicKey;\n // }\n\n // const exBitmapAccountInfos = await this.fetchExBitmaps({\n // connection,\n // exBitmapAddress: Object.values(exBitmapAddress),\n // batchRequest,\n // });\n\n // const programIds: PublicKey[] = [];\n\n // const poolsInfo: ReturnTypeFetchMultiplePoolInfos = {};\n\n // const updateRewardInfos: ClmmPoolRewardInfo[] = [];\n\n // for (let index = 0; index < poolKeys.length; index++) {\n // const apiPoolInfo = poolKeys[index];\n // const accountInfo = poolAccountInfos[index];\n // const exBitmapInfo = exBitmapAccountInfos[exBitmapAddress[apiPoolInfo.id].toString()];\n\n // if (accountInfo === null) continue;\n\n // const layoutAccountInfo = PoolInfoLayout.decode(accountInfo.data);\n // poolsInfo[apiPoolInfo.id] = {\n // state: {\n // id: new PublicKey(apiPoolInfo.id),\n // mintA: {\n // programId: new PublicKey(apiPoolInfo.mintA.programId),\n // mint: layoutAccountInfo.mintA,\n // vault: layoutAccountInfo.vaultA,\n // decimals: layoutAccountInfo.mintDecimalsA,\n // },\n // mintB: {\n // programId: new PublicKey(apiPoolInfo.mintB.programId),\n // mint: layoutAccountInfo.mintB,\n // vault: layoutAccountInfo.vaultB,\n // decimals: layoutAccountInfo.mintDecimalsB,\n // },\n // observationId: layoutAccountInfo.observationId,\n // ammConfig: {\n // ...apiPoolInfo.config,\n // fundOwner: apiPoolInfo.config.id,\n // id: new PublicKey(apiPoolInfo.config.id),\n // },\n\n // creator: layoutAccountInfo.creator,\n // programId: accountInfo.owner,\n // version: 6,\n\n // tickSpacing: layoutAccountInfo.tickSpacing,\n // liquidity: layoutAccountInfo.liquidity,\n // sqrtPriceX64: layoutAccountInfo.sqrtPriceX64,\n // currentPrice: SqrtPriceMath.sqrtPriceX64ToPrice(\n // layoutAccountInfo.sqrtPriceX64,\n // layoutAccountInfo.mintDecimalsA,\n // layoutAccountInfo.mintDecimalsB,\n // ),\n // tickCurrent: layoutAccountInfo.tickCurrent,\n // observationIndex: layoutAccountInfo.observationIndex,\n // observationUpdateDuration: layoutAccountInfo.observationUpdateDuration,\n // feeGrowthGlobalX64A: layoutAccountInfo.feeGrowthGlobalX64A,\n // feeGrowthGlobalX64B: layoutAccountInfo.feeGrowthGlobalX64B,\n // protocolFeesTokenA: layoutAccountInfo.protocolFeesTokenA,\n // protocolFeesTokenB: layoutAccountInfo.protocolFeesTokenB,\n // swapInAmountTokenA: layoutAccountInfo.swapInAmountTokenA,\n // swapOutAmountTokenB: layoutAccountInfo.swapOutAmountTokenB,\n // swapInAmountTokenB: layoutAccountInfo.swapInAmountTokenB,\n // swapOutAmountTokenA: layoutAccountInfo.swapOutAmountTokenA,\n // tickArrayBitmap: layoutAccountInfo.tickArrayBitmap,\n\n // rewardInfos: await PoolUtils.updatePoolRewardInfos({\n // connection,\n // apiPoolInfo,\n // chainTime,\n // poolLiquidity: layoutAccountInfo.liquidity,\n // rewardInfos: layoutAccountInfo.rewardInfos.filter((i) => !i.tokenMint.equals(PublicKey.default)),\n // }),\n\n // day: apiPoolInfo.day,\n // week: apiPoolInfo.week,\n // month: apiPoolInfo.month,\n // tvl: apiPoolInfo.tvl,\n // lookupTableAccount: new PublicKey(apiPoolInfo.lookupTableAccount),\n\n // startTime: layoutAccountInfo.startTime.toNumber(),\n\n // exBitmapInfo,\n // },\n // };\n\n // if (ownerInfo) {\n // updateRewardInfos.push(\n // ...poolsInfo[apiPoolInfo.id].state.rewardInfos.filter((i) => i.creator.equals(ownerInfo.wallet)),\n // );\n // }\n\n // if (!programIds.find((i) => i.equals(accountInfo.owner))) programIds.push(accountInfo.owner);\n // }\n\n // if (ownerInfo) {\n // const allMint = ownerInfo.tokenAccounts\n // .filter((i) => i.accountInfo.amount.eq(new BN(1)))\n // .map((i) => i.accountInfo.mint);\n // const allPositionKey: PublicKey[] = [];\n // for (const itemMint of allMint) {\n // for (const itemProgramId of programIds) {\n // allPositionKey.push(getPdaPersonalPositionAddress(itemProgramId, itemMint).publicKey);\n // }\n // }\n // const positionAccountInfos = await getMultipleAccountsInfo(connection, allPositionKey, { batchRequest });\n\n // const keyToTickArrayAddress: { [key: string]: PublicKey } = {};\n // for (const itemAccountInfo of positionAccountInfos) {\n // if (itemAccountInfo === null) continue;\n // const position = PositionInfoLayout.decode(itemAccountInfo.data);\n // const itemPoolId = position.poolId.toString();\n // const poolInfoA = poolsInfo[itemPoolId];\n // if (poolInfoA === undefined) continue;\n\n // const poolInfo = poolInfoA.state;\n\n // const priceLower = TickUtils._getTickPriceLegacy({\n // poolInfo,\n // tick: position.tickLower,\n // baseIn: true,\n // });\n // const priceUpper = TickUtils._getTickPriceLegacy({\n // poolInfo,\n // tick: position.tickUpper,\n // baseIn: true,\n // });\n // const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(\n // poolInfo.sqrtPriceX64,\n // priceLower.tickSqrtPriceX64,\n // priceUpper.tickSqrtPriceX64,\n // position.liquidity,\n // false,\n // );\n\n // const leverage = 1 / (1 - Math.sqrt(Math.sqrt(priceLower.price.div(priceUpper.price).toNumber())));\n\n // poolsInfo[itemPoolId].positionAccount = [\n // ...(poolsInfo[itemPoolId].positionAccount ?? []),\n // {\n // poolId: position.poolId,\n // nftMint: position.nftMint,\n\n // priceLower: priceLower.price,\n // priceUpper: priceUpper.price,\n // amountA,\n // amountB,\n // tickLower: position.tickLower,\n // tickUpper: position.tickUpper,\n // liquidity: position.liquidity,\n // feeGrowthInsideLastX64A: position.feeGrowthInsideLastX64A,\n // feeGrowthInsideLastX64B: position.feeGrowthInsideLastX64B,\n // tokenFeesOwedA: position.tokenFeesOwedA,\n // tokenFeesOwedB: position.tokenFeesOwedB,\n // rewardInfos: position.rewardInfos.map((i) => ({\n // ...i,\n // pendingReward: new BN(0),\n // })),\n\n // leverage,\n // tokenFeeAmountA: new BN(0),\n // tokenFeeAmountB: new BN(0),\n // },\n // ];\n\n // const tickArrayLowerAddress = TickUtils.getTickArrayAddressByTick(\n // poolsInfo[itemPoolId].state.programId,\n // position.poolId,\n // position.tickLower,\n // poolsInfo[itemPoolId].state.tickSpacing,\n // );\n // const tickArrayUpperAddress = TickUtils.getTickArrayAddressByTick(\n // poolsInfo[itemPoolId].state.programId,\n // position.poolId,\n // position.tickUpper,\n // poolsInfo[itemPoolId].state.tickSpacing,\n // );\n // keyToTickArrayAddress[\n // `${poolsInfo[itemPoolId].state.programId.toString()}-${position.poolId.toString()}-${position.tickLower}`\n // ] = tickArrayLowerAddress;\n // keyToTickArrayAddress[\n // `${poolsInfo[itemPoolId].state.programId.toString()}-${position.poolId.toString()}-${position.tickUpper}`\n // ] = tickArrayUpperAddress;\n // }\n\n // if (updateOwnerRewardAndFee) {\n // const tickArrayKeys = Object.values(keyToTickArrayAddress);\n // const tickArrayDatas = await getMultipleAccountsInfo(connection, tickArrayKeys, { batchRequest });\n // const tickArrayLayout: { [key: string]: TickArray } = {};\n // for (let index = 0; index < tickArrayKeys.length; index++) {\n // const tickArrayData = tickArrayDatas[index];\n // if (tickArrayData === null) continue;\n // const key = tickArrayKeys[index];\n // tickArrayLayout[key.toString()] = {\n // address: key,\n // ...TickArrayLayout.decode(tickArrayData.data),\n // };\n // }\n\n // for (const { state, positionAccount } of Object.values(poolsInfo)) {\n // if (!positionAccount) continue;\n // for (const itemPA of positionAccount) {\n // const keyLower = `${state.programId.toString()}-${state.id.toString()}-${itemPA.tickLower}`;\n // const keyUpper = `${state.programId.toString()}-${state.id.toString()}-${itemPA.tickUpper}`;\n // const tickArrayLower = tickArrayLayout[keyToTickArrayAddress[keyLower].toString()];\n // const tickArrayUpper = tickArrayLayout[keyToTickArrayAddress[keyUpper].toString()];\n // const tickLowerState: Tick =\n // tickArrayLower.ticks[TickUtils.getTickOffsetInArray(itemPA.tickLower, state.tickSpacing)];\n // const tickUpperState: Tick =\n // tickArrayUpper.ticks[TickUtils.getTickOffsetInArray(itemPA.tickUpper, state.tickSpacing)];\n // const { tokenFeeAmountA, tokenFeeAmountB } = PositionUtils.GetPositionFees(\n // state,\n // itemPA,\n // tickLowerState,\n // tickUpperState,\n // );\n // const rewardInfos = PositionUtils.GetPositionRewards(state, itemPA, tickLowerState, tickUpperState);\n // itemPA.tokenFeeAmountA =\n // tokenFeeAmountA.gte(ZERO) && tokenFeeAmountA.lt(U64_IGNORE_RANGE) ? tokenFeeAmountA : ZERO\n // itemPA.tokenFeeAmountB =\n // tokenFeeAmountB.gte(ZERO) && tokenFeeAmountA.lt(U64_IGNORE_RANGE) ? tokenFeeAmountB : ZERO\n // for (let i = 0; i < rewardInfos.length; i++) {\n // itemPA.rewardInfos[i].pendingReward = rewardInfos[i].gte(ZERO) ? rewardInfos[i] : ZERO;\n // }\n // }\n // }\n // }\n // }\n\n // if (updateRewardInfos.length > 0) {\n // const vaults = updateRewardInfos.map((i) => i.tokenVault);\n // const rewardVaultInfos = await getMultipleAccountsInfo(connection, vaults, { batchRequest });\n // const rewardVaultAmount: { [mint: string]: BN } = {};\n // for (let index = 0; index < vaults.length; index++) {\n // const valutKey = vaults[index].toString();\n // const itemRewardVaultInfo = rewardVaultInfos[index];\n // if (itemRewardVaultInfo === null) continue;\n // const info = splAccountLayout.decode(itemRewardVaultInfo.data);\n // rewardVaultAmount[valutKey] = info.amount;\n // }\n // for (const item of updateRewardInfos) {\n // const vaultAmount = rewardVaultAmount[item.tokenVault.toString()];\n // item.remainingRewards =\n // vaultAmount !== undefined ? vaultAmount.sub(item.rewardTotalEmissioned.sub(item.rewardClaimed)) : ZERO;\n // }\n // }\n\n // return poolsInfo;\n // }\n\n static async fetchMultiplePoolTickArrays({\n connection,\n poolKeys,\n batchRequest,\n }: {\n connection: Connection;\n poolKeys: ClmmPoolInfo[];\n batchRequest?: boolean;\n }): Promise<ReturnTypeFetchMultiplePoolTickArrays> {\n const tickArraysToPoolId: { [key: string]: PublicKey } = {};\n const tickArrays: { pubkey: PublicKey }[] = [];\n for (const itemPoolInfo of poolKeys) {\n const currentTickArrayStartIndex = TickUtils.getTickArrayStartIndexByTick(\n itemPoolInfo.tickCurrent,\n itemPoolInfo.tickSpacing,\n );\n const startIndexArray = TickUtils.getInitializedTickArrayInRange(\n itemPoolInfo.tickArrayBitmap,\n itemPoolInfo.exBitmapInfo,\n itemPoolInfo.tickSpacing,\n currentTickArrayStartIndex,\n 7,\n );\n for (const itemIndex of startIndexArray) {\n const { publicKey: tickArrayAddress } = getPdaTickArrayAddress(\n itemPoolInfo.programId,\n itemPoolInfo.id,\n itemIndex,\n );\n tickArrays.push({ pubkey: tickArrayAddress });\n tickArraysToPoolId[tickArrayAddress.toString()] = itemPoolInfo.id;\n }\n }\n\n const fetchedTickArrays = await getMultipleAccountsInfoWithCustomFlags(connection, tickArrays, { batchRequest });\n\n const tickArrayCache: ReturnTypeFetchMultiplePoolTickArrays = {};\n\n for (const itemAccountInfo of fetchedTickArrays) {\n if (!itemAccountInfo.accountInfo) continue;\n const poolId = tickArraysToPoolId[itemAccountInfo.pubkey.toString()];\n if (!poolId) continue;\n if (tickArrayCache[poolId.toString()] === undefined) tickArrayCache[poolId.toString()] = {};\n\n const accountLayoutData = TickArrayLayout.decode(itemAccountInfo.accountInfo.data);\n\n tickArrayCache[poolId.toString()][accountLayoutData.startTickIndex] = {\n ...accountLayoutData,\n address: itemAccountInfo.pubkey,\n };\n }\n return tickArrayCache;\n }\n\n // deprecated, new api doesn't need\n static async fetchPoolsAccountPosition({\n pools,\n connection,\n ownerInfo,\n batchRequest = false,\n updateOwnerRewardAndFee = true,\n }: {\n pools: SDKParsedConcentratedInfo[];\n connection: Connection;\n ownerInfo: { wallet: PublicKey; tokenAccounts: TokenAccountRaw[] };\n batchRequest?: boolean;\n updateOwnerRewardAndFee?: boolean;\n }): Promise<SDKParsedConcentratedInfo[]> {\n const programIds: PublicKey[] = [];\n\n for (let index = 0; index < pools.length; index++) {\n const accountInfo = pools[index];\n\n if (accountInfo === null) continue;\n\n if (!programIds.find((i) => i.equals(accountInfo.state.programId))) programIds.push(accountInfo.state.programId);\n }\n\n if (ownerInfo) {\n const allMint = ownerInfo.tokenAccounts.map((i) => i.accountInfo.mint);\n const allPositionKey: PublicKey[] = [];\n for (const itemMint of allMint) {\n for (const itemProgramId of programIds) {\n allPositionKey.push(getPdaPersonalPositionAddress(itemProgramId, itemMint).publicKey);\n }\n }\n const positionAccountInfos = await getMultipleAccountsInfo(connection, allPositionKey, { batchRequest });\n const keyToTickArrayAddress: { [key: string]: PublicKey } = {};\n for (const itemAccountInfo of positionAccountInfos) {\n if (itemAccountInfo === null) continue;\n // TODO: add check\n\n const position = PositionInfoLayout.decode(itemAccountInfo.data);\n const itemPoolId = position.poolId.toString();\n const poolInfoA = pools.find((pool) => pool.state.id.toBase58() === itemPoolId);\n if (poolInfoA === undefined) continue;\n\n const poolInfo = poolInfoA.state;\n\n const priceLower = TickUtils._getTickPriceLegacy({\n poolInfo,\n tick: position.tickLower,\n baseIn: true,\n });\n const priceUpper = TickUtils._getTickPriceLegacy({\n poolInfo,\n tick: position.tickUpper,\n baseIn: true,\n });\n const { amountA, amountB } = LiquidityMath.getAmountsFromLiquidity(\n poolInfo.sqrtPriceX64,\n priceLower.tickSqrtPriceX64,\n priceUpper.tickSqrtPriceX64,\n position.liquidity,\n false,\n );\n\n const leverage = 1 / (1 - Math.sqrt(Math.sqrt(priceLower.price.div(priceUpper.price).toNumber())));\n\n poolInfoA.positionAccount = [\n ...(poolInfoA.positionAccount ?? []),\n {\n poolId: position.poolId,\n nftMint: position.nftMint,\n\n priceLower: priceLower.price,\n priceUpper: priceUpper.price,\n amountA,\n amountB,\n tickLower: position.tickLower,\n tickUpper: position.tickUpper,\n liquidity: position.liquidity,\n feeGrowthInsideLastX64A: position.feeGrowthInsideLastX64A,\n feeGrowthInsideLastX64B: position.feeGrowthInsideLastX64B,\n tokenFeesOwedA: position.tokenFeesOwedA,\n tokenFeesOwedB: position.tokenFeesOwedB,\n rewardInfos: position.rewardInfos.map((i) => ({\n ...i,\n pendingReward: new BN(0),\n })),\n\n leverage,\n tokenFeeAmountA: new BN(0),\n tokenFeeAmountB: new BN(0),\n },\n ];\n\n const tickArrayLowerAddress = await TickUtils.getTickArrayAddressByTick(\n poolInfoA.state.programId,\n position.poolId,\n position.tickLower,\n poolInfoA.state.tickSpacing,\n );\n const tickArrayUpperAddress = await TickUtils.getTickArrayAddressByTick(\n poolInfoA.state.programId,\n position.poolId,\n position.tickUpper,\n poolInfoA.state.tickSpacing,\n );\n keyToTickArrayAddress[\n `${poolInfoA.state.programId.toString()}-${position.poolId.toString()}-${position.tickLower}`\n ] = tickArrayLowerAddress;\n keyToTickArrayAddress[\n `${poolInfoA.state.programId.toString()}-${position.poolId.toString()}-${position.tickUpper}`\n ] = tickArrayUpperAddress;\n }\n\n if (updateOwnerRewardAndFee) {\n const tickArrayKeys = Object.values(keyToTickArrayAddress);\n const tickArrayDatas = await getMultipleAccountsInfo(connection, tickArrayKeys, { batchRequest });\n const tickArrayLayout = {};\n for (let index = 0; index < tickArrayKeys.length; index++) {\n const tickArrayData = tickArrayDatas[index];\n if (tickArrayData === null) continue;\n const key = tickArrayKeys[index].toString();\n tickArrayLayout[key] = TickArrayLayout.decode(tickArrayData.data);\n }\n\n for (const { state, positionAccount } of pools) {\n if (!positionAccount) continue;\n for (const itemPA of positionAccount) {\n const keyLower = `${state.programId.toString()}-${state.id.toString()}-${itemPA.tickLower}`;\n const keyUpper = `${state.programId.toString()}-${state.id.toString()}-${itemPA.tickUpper}`;\n const tickArrayLower = tickArrayLayout[keyToTickArrayAddress[keyLower].toString()];\n const tickArrayUpper = tickArrayLayout[keyToTickArrayAddress[keyUpper].toString()];\n const tickLowerState: Tick =\n tickArrayLower.ticks[TickUtils.getTickOffsetInArray(itemPA.tickLower, state.tickSpacing)];\n const tickUpperState: Tick =\n tickArrayUpper.ticks[TickUtils.getTickOffsetInArray(itemPA.tickUpper, state.tickSpacing)];\n const { tokenFeeAmountA, tokenFeeAmountB } = await PositionUtils.GetPositionFees(\n state,\n itemPA,\n tickLowerState,\n tickUpperState,\n );\n const rewardInfos = await PositionUtils.GetPositionRewards(state, itemPA, tickLowerState, tickUpperState);\n itemPA.tokenFeeAmountA = tokenFeeAmountA.gte(new BN(0)) ? tokenFeeAmountA : new BN(0);\n itemPA.tokenFeeAmountB = tokenFeeAmountB.gte(new BN(0)) ? tokenFeeAmountB : new BN(0);\n for (let i = 0; i < rewardInfos.length; i++) {\n itemPA.rewardInfos[i].pendingReward = rewardInfos[i].gte(new BN(0)) ? rewardInfos[i] : new BN(0);\n }\n }\n }\n }\n }\n return pools;\n }\n\n static computeAmountOut({\n poolInfo,\n tickArrayCache,\n baseMint,\n token2022Infos,\n epochInfo,\n amountIn,\n slippage,\n priceLimit = new Decimal(0),\n }: {\n poolInfo: ClmmPoolInfo;\n tickArrayCache: { [key: string]: TickArray };\n baseMint: PublicKey;\n\n token2022Infos: ReturnTypeFetchMultipleMintInfos;\n epochInfo: EpochInfo;\n\n amountIn: BN;\n slippage: number;\n priceLimit?: Decimal;\n }): ReturnTypeComputeAmountOut {\n let sqrtPriceLimitX64: BN;\n if (priceLimit.equals(new Decimal(0))) {\n sqrtPriceLimitX64 = baseMint.equals(poolInfo.mintA.mint)\n ? MIN_SQRT_PRICE_X64.add(new BN(1))\n : MAX_SQRT_PRICE_X64.sub(new BN(1));\n } else {\n sqrtPriceLimitX64 = SqrtPriceMath.priceToSqrtPriceX64(\n priceLimit,\n poolInfo.mintA.decimals,\n poolInfo.mintB.decimals,\n );\n }\n\n const realAmountIn = getTransferAmountFee(\n amountIn,\n token2022Infos[baseMint.toString()]?.feeConfig,\n epochInfo,\n false,\n );\n\n const {\n expectedAmountOut: _expectedAmountOut,\n remainingAccounts,\n executionPrice: _executionPriceX64,\n feeAmount,\n } = PoolUtils.getOutputAmountAndRemainAccounts(\n poolInfo,\n tickArrayCache,\n baseMint,\n realAmountIn.amount.sub(realAmountIn.fee ?? ZERO),\n sqrtPriceLimitX64,\n );\n\n const outMint = poolInfo.mintA.mint.equals(baseMint) ? poolInfo.mintB.mint : poolInfo.mintA.mint;\n const amountOut = getTransferAmountFee(\n _expectedAmountOut,\n token2022Infos[outMint.toString()]?.feeConfig,\n epochInfo,\n false,\n );\n\n const _executionPrice = SqrtPriceMath.sqrtPriceX64ToPrice(\n _executionPriceX64,\n poolInfo.mintA.decimals,\n poolInfo.mintB.decimals,\n );\n const executionPrice = baseMint.equals(poolInfo.mintA.mint) ? _executionPrice : new Decimal(1).div(_executionPrice);\n\n const _minAmountOut = _expectedAmountOut\n .mul(new BN(Math.floor((1 - slippage) * 10000000000)))\n .div(new BN(10000000000));\n const minAmountOut = getTransferAmountFee(\n _minAmountOut,\n token2022Infos[outMint.toString()]?.feeConfig,\n epochInfo,\n false,\n );\n\n const poolPrice = poolInfo.mintA.mint.equals(baseMint)\n ? poolInfo.currentPrice\n : new Decimal(1).div(poolInfo.currentPrice);\n\n const _numerator = new Decimal(executionPrice).sub(poolPrice).abs();\n const _denominator = poolPrice;\n const priceImpact = new Percent(\n new Decimal(_numerator).mul(10 ** 15).toFixed(0),\n new Decimal(_denominator).mul(10 ** 15).toFixed(0),\n );\n\n return {\n realAmountIn,\n amountOut,\n minAmountOut,\n expirationTime: minExpirationTime(realAmountIn.expirationTime, amountOut.expirationTime),\n currentPrice: poolInfo.currentPrice,\n executionPrice,\n priceImpact,\n fee: feeAmount,\n\n remainingAccounts,\n };\n }\n\n static async computeAmountOutFormat({\n poolInfo,\n tickArrayCache,\n token2022Infos,\n amountIn,\n tokenOut: _tokenOut,\n slippage,\n epochInfo,\n }: {\n poolInfo: ClmmPoolInfo;\n tickArrayCache: { [key: string]: TickArray };\n token2022Infos: ReturnTypeFetchMultipleMintInfos;\n amountIn: TokenAmount;\n tokenOut: Token;\n slippage: Percent;\n epochInfo: EpochInfo;\n }): Promise<ReturnTypeComputeAmountOutFormat> {\n const inputMint = amountIn.token.equals(Token.WSOL) ? WSOLMint : amountIn.token.mint;\n const _slippage = slippage.numerator.toNumber() / slippage.denominator.toNumber();\n const tokenOut = _tokenOut.mint.equals(SOLMint)\n ? new Token({ mint: \"sol\", decimals: SOL_INFO.decimals })\n : _tokenOut;\n\n const {\n realAmountIn: _realAmountIn,\n amountOut: _amountOut,\n minAmountOut: _minAmountOut,\n expirationTime,\n currentPrice,\n executionPrice,\n priceImpact,\n fee,\n remainingAccounts,\n } = await PoolUtils.computeAmountOut({\n poolInfo,\n tickArrayCache,\n baseMint: inputMint,\n amountIn: amountIn.raw,\n slippage: _slippage,\n token2022Infos,\n epochInfo,\n });\n\n const realAmountIn = {\n ..._realAmountIn,\n amount: new TokenAmount(amountIn.token, _realAmountIn.amount),\n fee: _realAmountIn.fee === undefined ? undefined : new TokenAmount(amountIn.token, _realAmountIn.fee),\n };\n\n const amountOut = {\n ..._amountOut,\n amount: new TokenAmount(tokenOut, _amountOut.amount),\n fee: _amountOut.fee === undefined ? undefined : new TokenAmount(tokenOut, _amountOut.fee),\n };\n const minAmountOut = {\n ..._minAmountOut,\n amount: new TokenAmount(tokenOut, _minAmountOut.amount),\n fee: _minAmountOut.fee === undefined ? undefined : new TokenAmount(tokenOut, _minAmountOut.fee),\n };\n\n const _currentPrice = new Price({\n baseToken: amountIn.token,\n denominator: new BN(10).pow(new BN(20 + amountIn.token.decimals)),\n quoteToken: tokenOut,\n numerator: currentPrice.mul(new Decimal(10 ** (20 + tokenOut.decimals))).toFixed(0),\n });\n const _executionPrice = new Price({\n baseToken: amountIn.token,\n denominator: new BN(10).pow(new BN(20 + amountIn.token.decimals)),\n quoteToken: tokenOut,\n numerator: executionPrice.mul(new Decimal(10 ** (20 + tokenOut.decimals))).toFixed(0),\n });\n const _fee = new TokenAmount(amountIn.token, fee);\n\n return {\n realAmountIn,\n amountOut,\n minAmountOut,\n expirationTime,\n currentPrice: _currentPrice,\n executionPrice: _executionPrice,\n priceImpact,\n fee: _fee,\n remainingAccounts,\n };\n }\n\n static estimateAprsForPriceRangeMultiplier({\n poolInfo,\n aprType,\n positionTickLowerIndex,\n positionTickUpperIndex,\n }: {\n poolInfo: ApiV3PoolInfoConcentratedItem;\n aprType: \"day\" | \"week\" | \"month\";\n\n positionTickLowerIndex: number;\n positionTickUpperIndex: number;\n }): {\n feeApr: number;\n rewardsApr: number[];\n apr: number;\n } {\n const aprInfo = poolInfo[aprType];\n\n const priceLower = TickUtils.getTickPrice({\n poolInfo,\n tick: positionTickLowerIndex,\n baseIn: true,\n }).price.toNumber();\n const priceUpper = TickUtils.getTickPrice({\n poolInfo,\n tick: positionTickUpperIndex,\n baseIn: true,\n }).price.toNumber();\n\n const _minPrice = Math.max(priceLower, aprInfo.priceMin);\n const _maxPrice = Math.min(priceUpper, aprInfo.priceMax);\n\n const sub = _maxPrice - _minPrice;\n\n const userRange = priceUpper - priceLower;\n const tradeRange = aprInfo.priceMax - aprInfo.priceMin;\n\n let p: number;\n\n if (sub <= 0) p = 0;\n else if (userRange === sub) p = tradeRange / sub;\n else if (tradeRange === sub) p = sub / userRange;\n else p = (sub / tradeRange) * (sub / userRange);\n\n return {\n feeApr: aprInfo.feeApr * p,\n rewardsApr: [aprInfo.rewardApr[0] ?? 0 * p, aprInfo.rewardApr[1] ?? 0 * p, aprInfo.rewardApr[2] ?? 0 * p],\n apr: aprInfo.apr * p,\n };\n }\n\n static estimateAprsForPriceRangeDelta({\n poolInfo,\n poolLiquidity,\n aprType,\n mintPrice,\n liquidity,\n positionTickLowerIndex,\n positionTickUpperIndex,\n chainTime,\n }: {\n poolInfo: ApiV3PoolInfoConcentratedItem;\n poolLiquidity: BN;\n aprType: \"day\" | \"week\" | \"month\";\n\n mintPrice: { [mint: string]: { value: number } };\n\n liquidity: BN;\n positionTickLowerIndex: number;\n positionTickUpperIndex: number;\n\n chainTime: number;\n }): {\n feeApr: number;\n rewardsApr: number[];\n apr: number;\n } {\n const aprTypeDay = aprType === \"day\" ? 1 : aprType === \"week\" ? 7 : aprType === \"month\" ? 30 : 0;\n const aprInfo = poolInfo[aprType];\n const mintPriceA = mintPrice[solToWSol(poolInfo.mintA.address).toString()];\n const mintPriceB = mintPrice[solToWSol(poolInfo.mintB.address).toString()];\n const mintDecimalsA = poolInfo.mintA.decimals;\n const mintDecimalsB = poolInfo.mintB.decimals;\n\n if (!aprInfo || !mintPriceA || !mintPriceB) return { feeApr: 0, rewardsApr: [0, 0, 0], apr: 0 };\n\n const sqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(\n new Decimal(poolInfo.price),\n poolInfo.mintA.decimals,\n poolInfo.mintB.decimals,\n );\n\n const sqrtPriceX64A = SqrtPriceMath.getSqrtPriceX64FromTick(positionTickLowerIndex);\n const sqrtPriceX64B = SqrtPriceMath.getSqrtPriceX64FromTick(positionTickUpperIndex);\n\n const { amountSlippageA: poolLiquidityA, amountSlippageB: poolLiquidityB } =\n LiquidityMath.getAmountsFromLiquidityWithSlippage(\n sqrtPriceX64,\n sqrtPriceX64A,\n sqrtPriceX64B,\n poolLiquidity,\n false,\n false,\n 0,\n );\n\n const { amountSlippageA: userLiquidityA, amountSlippageB: userLiquidityB } =\n LiquidityMath.getAmountsFromLiquidityWithSlippage(\n sqrtPriceX64,\n sqrtPriceX64A,\n sqrtPriceX64B,\n liquidity,\n false,\n false,\n 0,\n );\n\n const poolTvl = new Decimal(poolLiquidityA.toString())\n .div(new Decimal(10).pow(mintDecimalsA))\n .mul(mintPriceA.value)\n .add(new Decimal(poolLiquidityB.toString()).div(new Decimal(10).pow(mintDecimalsB)).mul(mintPriceB.value));\n const userTvl = new Decimal(userLiquidityA.toString())\n .div(new Decimal(10).pow(mintDecimalsA))\n .mul(mintPriceA.value)\n .add(new Decimal(userLiquidityB.toString()).div(new Decimal(10).pow(mintDecimalsB)).mul(mintPriceB.value));\n\n const p = userTvl.div(poolTvl.add(userTvl)).div(userTvl);\n\n const feesPerYear = new Decimal(aprInfo.volumeFee).mul(365).div(aprTypeDay);\n const feeApr = feesPerYear.mul(p).mul(100).toNumber();\n\n const SECONDS_PER_YEAR = 3600 * 24 * 365;\n\n const rewardsApr = poolInfo.rewardDefaultInfos.map((i) => {\n const iDecimal = i.mint.decimals;\n const iPrice = mintPrice[i.mint.address];\n\n if (\n chainTime < ((i as any).startTime ?? 0) ||\n chainTime > ((i as any).endTime ?? 0) ||\n !i.perSecond ||\n !iPrice ||\n iDecimal === undefined\n )\n return 0;\n\n return new Decimal(iPrice.value)\n .mul(new Decimal(i.perSecond).mul(SECONDS_PER_YEAR))\n .div(new Decimal(10).pow(iDecimal))\n .mul(p)\n .mul(100)\n .toNumber();\n });\n\n return {\n feeApr,\n rewardsApr,\n apr: feeApr + rewardsApr.reduce((a, b) => a + b, 0),\n };\n }\n\n static getLiquidityAmountOutFromAmountIn({\n poolInfo,\n inputA,\n tickLower,\n tickUpper,\n amount,\n slippage,\n add,\n epochInfo,\n amountHasFee,\n }: {\n poolInfo: ApiV3PoolInfoConcentratedItem;\n inputA: boolean;\n tickLower: number;\n tickUpper: number;\n amount: BN;\n slippage: number;\n add: boolean;\n epochInfo: EpochInfo;\n amountHasFee: boolean;\n }): Promise<ReturnTypeGetLiquidityAmountOut> {\n const sqrtPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(\n new Decimal(poolInfo.price),\n poolInfo.mintA.decimals,\n poolInfo.mintB.decimals,\n );\n const sqrtPriceX64A = SqrtPriceMath.getSqrtPriceX64FromTick(tickLower);\n const sqrtPriceX64B = SqrtPriceMath.getSqrtPriceX64FromTick(tickUpper);\n\n const coefficient = add ? 1 - slippage : 1 + slippage;\n const addFeeAmount = getTransferAmountFeeV2(\n amount,\n poolInfo[inputA ? \"mintA\" : \"mintB\"].extensions?.feeConfig,\n epochInfo,\n !amountHasFee,\n );\n const _amount = new BN(\n new Decimal(addFeeAmount.amount.sub(addFeeAmount.fee ?? ZERO).toString()).mul(coefficient).toFixed(0),\n );\n\n let liquidity: BN;\n if (sqrtPriceX64.lte(sqrtPriceX64A)) {\n liquidity = inputA\n ? LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceX64A, sqrtPriceX64B, _amount, !add)\n : new BN(0);\n } else if (sqrtPriceX64.lte(sqrtPriceX64B)) {\n const liquidity0 = LiquidityMath.getLiquidityFromTokenAmountA(sqrtPriceX64, sqrtPriceX64B, _amount, !add);\n const liquidity1 = LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceX64, _amount);\n liquidity = inputA ? liquidity0 : liquidity1;\n } else {\n liquidity = inputA\n ? new BN(0)\n : LiquidityMath.getLiquidityFromTokenAmountB(sqrtPriceX64A, sqrtPriceX64B, _amount);\n }\n\n return PoolUtils.getAmountsFromLiquidity({\n epochInfo,\n poolInfo,\n tickLower,\n tickUpper,\n liquidity,\n slippage,\n add,\n });\n }\n\n static async getAmountsFromLiquidity({\n epochInfo,\n poolInfo,\n tickLower,\n tickUpper,\n liquidity,\n slippage,\n add,\n }: {\n epochInfo: EpochInfo;\n poolInfo: ApiV3PoolInfoConcentratedItem;\n tickLower: number;\n tickUpper: number;\n liquidity: BN;\n slippage: number;\n add: boolean;\n }): Promise<ReturnTypeGetLiquidityAmountOut>