snapper-sdk
Version:
An SDK for building applications on top of Snapper.
1 lines • 903 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/raydium/liquidity/liquidity.ts","../../../src/common/accountInfo.ts","../../../src/common/bignumber.ts","../../../node_modules/decimal.js/decimal.mjs","../../../src/module/amount.ts","../../../src/common/logger.ts","../../../src/module/formatter.ts","../../../src/module/fraction.ts","../../../src/common/constant.ts","../../../src/raydium/token/constant.ts","../../../src/module/token.ts","../../../src/common/pubKey.ts","../../../src/module/currency.ts","../../../src/module/percent.ts","../../../src/module/price.ts","../../../src/common/utility.ts","../../../src/common/lodash.ts","../../../src/common/pda.ts","../../../src/common/txTool/txUtils.ts","../../../src/common/txTool/txType.ts","../../../src/common/programId.ts","../../../src/common/transfer.ts","../../../src/common/txTool/lookupTable.ts","../../../src/common/txTool/txTool.ts","../../../src/marshmallow/index.ts","../../../src/marshmallow/buffer-layout.ts","../../../src/raydium/farm/config.ts","../../../src/raydium/farm/layout.ts","../../../src/raydium/farm/util.ts","../../../src/raydium/account/layout.ts","../../../src/raydium/farm/instruction.ts","../../../src/raydium/clmm/instrument.ts","../../../src/raydium/clmm/utils/tick.ts","../../../src/raydium/clmm/utils/constants.ts","../../../src/raydium/clmm/utils/math.ts","../../../src/raydium/clmm/utils/util.ts","../../../src/raydium/clmm/utils/pda.ts","../../../src/raydium/clmm/utils/pool.ts","../../../src/raydium/clmm/utils/position.ts","../../../src/raydium/clmm/utils/tickQuery.ts","../../../src/raydium/clmm/utils/tickarrayBitmap.ts","../../../src/raydium/clmm/layout.ts","../../../src/raydium/moduleBase.ts","../../../src/raydium/token/layout.ts","../../../src/raydium/token/utils.ts","../../../src/raydium/liquidity/constant.ts","../../../src/raydium/liquidity/instruction.ts","../../../src/raydium/liquidity/layout.ts","../../../src/raydium/liquidity/stable.ts","../../../src/raydium/liquidity/utils.ts","../../../src/raydium/liquidity/serum.ts"],"sourcesContent":["import { PublicKey } from \"@solana/web3.js\";\nimport {\n AmmV4Keys,\n AmmV5Keys,\n ApiV3PoolInfoConcentratedItem,\n ApiV3PoolInfoStandardItem,\n FormatFarmInfoOutV6,\n} from \"../../api/type\";\nimport { AccountLayout, NATIVE_MINT, TOKEN_PROGRAM_ID } from \"@solana/spl-token\";\nimport { getMultipleAccountsInfoWithCustomFlags } from \"@/common/accountInfo\";\nimport { BN_ZERO, divCeil } from \"@/common/bignumber\";\nimport { getATAAddress } from \"@/common/pda\";\nimport { BNDivCeil } from \"@/common/transfer\";\nimport { MakeMultiTxData, MakeTxData } from \"@/common/txTool/txTool\";\nimport { InstructionType, TxVersion } from \"@/common/txTool/txType\";\nimport { Percent, Token, TokenAmount } from \"../../module\";\nimport {\n FARM_PROGRAM_TO_VERSION,\n FarmLedger,\n createAssociatedLedgerAccountInstruction,\n getAssociatedLedgerAccount,\n getFarmLedgerLayout,\n makeWithdrawInstructionV3,\n makeWithdrawInstructionV5,\n makeWithdrawInstructionV6,\n} from \"../../raydium/farm\";\nimport { ClmmInstrument } from \"../clmm/instrument\";\nimport ModuleBase, { ModuleBaseProps } from \"../moduleBase\";\nimport { toToken } from \"../token\";\nimport { ComputeBudgetConfig } from \"../type\";\nimport { LIQUIDITY_FEES_DENOMINATOR, LIQUIDITY_FEES_NUMERATOR } from \"./constant\";\nimport {\n createPoolV4InstructionV2,\n makeAMMSwapInstruction,\n makeAddLiquidityInstruction,\n removeLiquidityInstruction,\n} from \"./instruction\";\nimport { createPoolFeeLayout, liquidityStateV4Layout } from \"./layout\";\nimport { StableLayout, getDxByDyBaseIn, getDyByDxBaseIn, getStablePrice } from \"./stable\";\nimport {\n AddLiquidityParams,\n AmmRpcData,\n AmountSide,\n ComputeAmountInParam,\n ComputeAmountOutParam,\n CreatePoolAddress,\n CreatePoolParam,\n RemoveParams,\n SwapParam,\n} from \"./type\";\nimport { getAssociatedConfigId, getAssociatedPoolKeys, toAmmComputePoolInfo } from \"./utils\";\n\nimport BN from \"bn.js\";\nimport Decimal from \"decimal.js\";\nimport { WSOLMint } from \"@/common\";\n\nexport default class LiquidityModule extends ModuleBase {\n public stableLayout: StableLayout;\n\n constructor(params: ModuleBaseProps) {\n super(params);\n this.stableLayout = new StableLayout({ connection: this.scope.connection });\n }\n\n public async initLayout(): Promise<void> {\n await this.stableLayout.initStableModelLayout();\n }\n\n public async load(): Promise<void> {\n this.checkDisabled();\n }\n\n public computePairAmount({\n poolInfo,\n amount,\n // anotherToken,\n slippage,\n baseIn,\n }: {\n poolInfo: ApiV3PoolInfoStandardItem;\n amount: string | Decimal;\n slippage: Percent;\n baseIn?: boolean;\n }): { anotherAmount: TokenAmount; maxAnotherAmount: TokenAmount; minAnotherAmount: TokenAmount; liquidity: BN } {\n const inputAmount = new BN(new Decimal(amount).mul(10 ** poolInfo[baseIn ? \"mintA\" : \"mintB\"].decimals).toFixed(0));\n const _anotherToken = toToken(poolInfo[baseIn ? \"mintB\" : \"mintA\"]);\n\n const [baseReserve, quoteReserve] = [\n new BN(new Decimal(poolInfo.mintAmountA).mul(10 ** poolInfo.mintA.decimals).toString()),\n new BN(new Decimal(poolInfo.mintAmountB).mul(10 ** poolInfo.mintB.decimals).toString()),\n ];\n const lpAmount = new BN(\n new Decimal(poolInfo.lpAmount).mul(10 ** poolInfo.lpMint.decimals).toFixed(0, Decimal.ROUND_DOWN),\n );\n this.logDebug(\"baseReserve:\", baseReserve.toString(), \"quoteReserve:\", quoteReserve.toString());\n\n this.logDebug(\n \"tokenIn:\",\n baseIn ? poolInfo.mintA.symbol : poolInfo.mintB.symbol,\n \"amountIn:\",\n inputAmount.toString(),\n \"anotherToken:\",\n baseIn ? poolInfo.mintB.symbol : poolInfo.mintA.symbol,\n \"slippage:\",\n `${slippage.toSignificant()}%`,\n \"baseReserve\",\n baseReserve.toString(),\n \"quoteReserve\",\n quoteReserve.toString(),\n );\n\n // input is fixed\n const input = baseIn ? \"base\" : \"quote\";\n this.logDebug(\"input side:\", input);\n\n // round up\n let amountRaw = BN_ZERO;\n if (!inputAmount.isZero()) {\n amountRaw =\n input === \"base\"\n ? divCeil(inputAmount.mul(quoteReserve), baseReserve)\n : divCeil(inputAmount.mul(baseReserve), quoteReserve);\n }\n\n this.logDebug(\"amountRaw:\", amountRaw.toString(), \"lpAmount:\", lpAmount.toString());\n\n const liquidity = divCeil(inputAmount.mul(lpAmount), input === \"base\" ? baseReserve : quoteReserve);\n\n this.logDebug(\"liquidity:\", liquidity.toString());\n\n const _slippage = new Percent(new BN(1)).add(slippage);\n const _slippageMin = new Percent(new BN(1)).sub(slippage);\n const slippageAdjustedAmount = _slippage.mul(amountRaw).quotient;\n const slippageAdjustedMinAmount = _slippageMin.mul(amountRaw).quotient;\n\n const _anotherAmount = new TokenAmount(_anotherToken, amountRaw);\n const _maxAnotherAmount = new TokenAmount(_anotherToken, slippageAdjustedAmount);\n const _minAnotherAmount = new TokenAmount(_anotherToken, slippageAdjustedMinAmount);\n this.logDebug(\"anotherAmount:\", _anotherAmount.toFixed(), \"maxAnotherAmount:\", _maxAnotherAmount.toFixed());\n\n return {\n anotherAmount: _anotherAmount,\n maxAnotherAmount: _maxAnotherAmount,\n minAnotherAmount: _minAnotherAmount,\n liquidity,\n };\n }\n\n public async getAmmPoolKeys(poolId: string): Promise<AmmV4Keys | AmmV5Keys> {\n return ((await this.scope.api.fetchPoolKeysById({ idList: [poolId] })) as (AmmV4Keys | AmmV5Keys)[])[0];\n }\n\n public async addLiquidity<T extends TxVersion>(params: AddLiquidityParams<T>): Promise<MakeTxData<T>> {\n const {\n poolInfo,\n poolKeys: propPoolKeys,\n amountInA,\n amountInB,\n otherAmountMin,\n fixedSide,\n config,\n txVersion,\n computeBudgetConfig,\n } = params;\n\n if (this.scope.availability.addStandardPosition === false)\n this.logAndCreateError(\"add liquidity feature disabled in your region\");\n\n this.logDebug(\"amountInA:\", amountInA, \"amountInB:\", amountInB);\n if (amountInA.isZero() || amountInB.isZero())\n this.logAndCreateError(\"amounts must greater than zero\", \"amountInA & amountInB\", {\n amountInA: amountInA.toFixed(),\n amountInB: amountInB.toFixed(),\n });\n const { account } = this.scope;\n const { bypassAssociatedCheck, checkCreateATAOwner } = {\n // default\n ...{ bypassAssociatedCheck: false, checkCreateATAOwner: false },\n // custom\n ...config,\n };\n const [tokenA, tokenB] = [amountInA.token, amountInB.token];\n const tokenAccountA = await account.getCreatedTokenAccount({\n mint: tokenA.mint,\n associatedOnly: false,\n });\n const tokenAccountB = await account.getCreatedTokenAccount({\n mint: tokenB.mint,\n associatedOnly: false,\n });\n if (!tokenAccountA && !tokenAccountB)\n this.logAndCreateError(\"cannot found target token accounts\", \"tokenAccounts\", account.tokenAccounts);\n\n const lpTokenAccount = await account.getCreatedTokenAccount({\n mint: new PublicKey(poolInfo.lpMint.address),\n });\n\n const tokens = [tokenA, tokenB];\n const _tokenAccounts = [tokenAccountA, tokenAccountB];\n const rawAmounts = [amountInA.raw, amountInB.raw];\n\n // handle amount a & b and direction\n const sideA = amountInA.token.mint.toBase58() === poolInfo.mintA.address ? \"base\" : \"quote\";\n let _fixedSide: AmountSide = \"base\";\n if (![\"quote\", \"base\"].includes(sideA)) this.logAndCreateError(\"invalid fixedSide\", \"fixedSide\", fixedSide);\n if (sideA === \"quote\") {\n tokens.reverse();\n _tokenAccounts.reverse();\n rawAmounts.reverse();\n _fixedSide = fixedSide === \"a\" ? \"quote\" : \"base\";\n } else if (sideA === \"base\") {\n _fixedSide = fixedSide === \"a\" ? \"base\" : \"quote\";\n }\n\n const [baseToken, quoteToken] = tokens;\n const [baseTokenAccount, quoteTokenAccount] = _tokenAccounts;\n const [baseAmountRaw, quoteAmountRaw] = rawAmounts;\n\n const poolKeys = propPoolKeys ?? (await this.getAmmPoolKeys(poolInfo.id));\n\n const txBuilder = this.createTxBuilder();\n\n const { tokenAccount: _baseTokenAccount, ...baseInstruction } = await account.handleTokenAccount({\n side: \"in\",\n amount: baseAmountRaw,\n mint: baseToken.mint,\n tokenAccount: baseTokenAccount,\n bypassAssociatedCheck,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(baseInstruction);\n const { tokenAccount: _quoteTokenAccount, ...quoteInstruction } = await account.handleTokenAccount({\n side: \"in\",\n amount: quoteAmountRaw,\n mint: quoteToken.mint,\n tokenAccount: quoteTokenAccount,\n bypassAssociatedCheck,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(quoteInstruction);\n const { tokenAccount: _lpTokenAccount, ...lpInstruction } = await account.handleTokenAccount({\n side: \"out\",\n amount: 0,\n mint: new PublicKey(poolInfo.lpMint.address),\n tokenAccount: lpTokenAccount,\n bypassAssociatedCheck,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(lpInstruction);\n txBuilder.addInstruction({\n instructions: [\n makeAddLiquidityInstruction({\n poolInfo,\n poolKeys: poolKeys as AmmV4Keys | AmmV5Keys,\n userKeys: {\n baseTokenAccount: _baseTokenAccount!,\n quoteTokenAccount: _quoteTokenAccount!,\n lpTokenAccount: _lpTokenAccount!,\n owner: this.scope.ownerPubKey,\n },\n baseAmountIn: baseAmountRaw,\n quoteAmountIn: quoteAmountRaw,\n otherAmountMin: otherAmountMin.raw,\n fixedSide: _fixedSide,\n }),\n ],\n instructionTypes: [\n poolInfo.pooltype.includes(\"StablePool\")\n ? InstructionType.AmmV5AddLiquidity\n : InstructionType.AmmV4AddLiquidity,\n ],\n lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],\n });\n txBuilder.addCustomComputeBudget(computeBudgetConfig);\n if (txVersion === TxVersion.V0) (await txBuilder.buildV0()) as MakeTxData<T>;\n return txBuilder.build() as MakeTxData<T>;\n }\n\n public async removeLiquidity<T extends TxVersion>(params: RemoveParams<T>): Promise<Promise<MakeTxData<T>>> {\n if (this.scope.availability.removeStandardPosition === false)\n this.logAndCreateError(\"remove liquidity feature disabled in your region\");\n const {\n poolInfo,\n poolKeys: propPoolKeys,\n lpAmount,\n baseAmountMin,\n quoteAmountMin,\n config,\n txVersion,\n computeBudgetConfig,\n } = params;\n const poolKeys = propPoolKeys ?? (await this.getAmmPoolKeys(poolInfo.id));\n const [baseMint, quoteMint, lpMint] = [\n new PublicKey(poolInfo.mintA.address),\n new PublicKey(poolInfo.mintB.address),\n new PublicKey(poolInfo.lpMint.address),\n ];\n this.logDebug(\"lpAmount:\", lpAmount);\n this.logDebug(\"baseAmountMin:\", baseAmountMin);\n this.logDebug(\"quoteAmountMin:\", quoteAmountMin);\n if (lpAmount.isZero()) this.logAndCreateError(\"amount must greater than zero\", \"lpAmount\", lpAmount.toString());\n\n const { account } = this.scope;\n const lpTokenAccount = await account.getCreatedTokenAccount({\n mint: lpMint,\n associatedOnly: false,\n });\n if (!lpTokenAccount) this.logAndCreateError(\"cannot found lpTokenAccount\", \"tokenAccounts\", account.tokenAccounts);\n\n const baseTokenAccount = await account.getCreatedTokenAccount({\n mint: baseMint,\n });\n const quoteTokenAccount = await account.getCreatedTokenAccount({\n mint: quoteMint,\n });\n\n const txBuilder = this.createTxBuilder();\n const { bypassAssociatedCheck, checkCreateATAOwner } = {\n // default\n ...{ bypassAssociatedCheck: false, checkCreateATAOwner: false },\n // custom\n ...config,\n };\n\n const { tokenAccount: _baseTokenAccount, ...baseInstruction } = await account.handleTokenAccount({\n side: \"out\",\n amount: 0,\n mint: baseMint,\n tokenAccount: baseTokenAccount,\n bypassAssociatedCheck,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(baseInstruction);\n const { tokenAccount: _quoteTokenAccount, ...quoteInstruction } = await account.handleTokenAccount({\n side: \"out\",\n amount: 0,\n mint: quoteMint,\n tokenAccount: quoteTokenAccount,\n bypassAssociatedCheck,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(quoteInstruction);\n\n txBuilder.addInstruction({\n instructions: [\n removeLiquidityInstruction({\n poolInfo,\n poolKeys,\n userKeys: {\n lpTokenAccount: lpTokenAccount!,\n baseTokenAccount: _baseTokenAccount!,\n quoteTokenAccount: _quoteTokenAccount!,\n owner: this.scope.ownerPubKey,\n },\n lpAmount,\n baseAmountMin,\n quoteAmountMin,\n }),\n ],\n lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],\n instructionTypes: [\n poolInfo.pooltype.includes(\"StablePool\")\n ? InstructionType.AmmV5RemoveLiquidity\n : InstructionType.AmmV4RemoveLiquidity,\n ],\n });\n txBuilder.addCustomComputeBudget(computeBudgetConfig);\n if (txVersion === TxVersion.V0) return (await txBuilder.buildV0()) as MakeTxData<T>;\n return txBuilder.build() as MakeTxData<T>;\n }\n\n public async removeAllLpAndCreateClmmPosition<T extends TxVersion>({\n poolInfo,\n clmmPoolInfo,\n removeLpAmount,\n createPositionInfo,\n farmInfo,\n userFarmLpAmount,\n base,\n computeBudgetConfig,\n payer,\n userAuxiliaryLedgers,\n tokenProgram = TOKEN_PROGRAM_ID,\n checkCreateATAOwner = true,\n getEphemeralSigners,\n txVersion,\n }: {\n poolInfo: ApiV3PoolInfoStandardItem;\n clmmPoolInfo: ApiV3PoolInfoConcentratedItem;\n removeLpAmount: BN;\n createPositionInfo: {\n tickLower: number;\n tickUpper: number;\n baseAmount: BN;\n otherAmountMax: BN;\n };\n farmInfo?: FormatFarmInfoOutV6;\n userFarmLpAmount?: BN;\n userAuxiliaryLedgers?: PublicKey[];\n base: \"MintA\" | \"MintB\";\n payer?: PublicKey;\n computeBudgetConfig?: ComputeBudgetConfig;\n tokenProgram?: PublicKey;\n checkCreateATAOwner?: boolean;\n txVersion?: T;\n getEphemeralSigners?: (k: number) => any;\n }): Promise<MakeMultiTxData<T>> {\n if (\n this.scope.availability.removeStandardPosition === false ||\n this.scope.availability.createConcentratedPosition === false\n )\n this.logAndCreateError(\"remove liquidity or create position feature disabled in your region\");\n\n if (\n !(poolInfo.mintA.address === clmmPoolInfo.mintA.address || poolInfo.mintA.address === clmmPoolInfo.mintB.address)\n )\n throw Error(\"mint check error\");\n if (\n !(poolInfo.mintB.address === clmmPoolInfo.mintA.address || poolInfo.mintB.address === clmmPoolInfo.mintB.address)\n )\n throw Error(\"mint check error\");\n\n const txBuilder = this.createTxBuilder();\n txBuilder.addCustomComputeBudget(computeBudgetConfig);\n const mintToAccount: { [mint: string]: PublicKey } = {};\n for (const item of this.scope.account.tokenAccountRawInfos) {\n if (\n mintToAccount[item.accountInfo.mint.toString()] === undefined ||\n getATAAddress(this.scope.ownerPubKey, item.accountInfo.mint, TOKEN_PROGRAM_ID).publicKey.equals(item.pubkey)\n ) {\n mintToAccount[item.accountInfo.mint.toString()] = item.pubkey;\n }\n }\n\n const lpTokenAccount = mintToAccount[poolInfo.lpMint.address];\n if (lpTokenAccount === undefined) throw Error(\"find lp account error in trade accounts\");\n\n const amountIn = removeLpAmount.add(userFarmLpAmount ?? new BN(0));\n const mintBaseUseSOLBalance = poolInfo.mintA.address === Token.WSOL.mint.toString();\n const mintQuoteUseSOLBalance = poolInfo.mintB.address === Token.WSOL.mint.toString();\n\n const { account: baseTokenAccount, instructionParams: ownerTokenAccountBaseInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n tokenProgram: TOKEN_PROGRAM_ID,\n mint: new PublicKey(poolInfo.mintA.address),\n owner: this.scope.ownerPubKey,\n\n createInfo: mintBaseUseSOLBalance\n ? {\n payer: this.scope.ownerPubKey,\n }\n : undefined,\n skipCloseAccount: !mintBaseUseSOLBalance,\n notUseTokenAccount: mintBaseUseSOLBalance,\n associatedOnly: true,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(ownerTokenAccountBaseInstruction || {});\n if (baseTokenAccount === undefined) throw new Error(\"base token account not found\");\n\n const { account: quoteTokenAccount, instructionParams: ownerTokenAccountQuoteInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n tokenProgram: TOKEN_PROGRAM_ID,\n mint: new PublicKey(poolInfo.mintB.address),\n owner: this.scope.ownerPubKey,\n createInfo: mintQuoteUseSOLBalance\n ? {\n payer: this.scope.ownerPubKey!,\n amount: 0,\n }\n : undefined,\n skipCloseAccount: !mintQuoteUseSOLBalance,\n notUseTokenAccount: mintQuoteUseSOLBalance,\n associatedOnly: true,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(ownerTokenAccountQuoteInstruction || {});\n if (quoteTokenAccount === undefined) throw new Error(\"quote token account not found\");\n\n mintToAccount[poolInfo.mintA.address] = baseTokenAccount;\n mintToAccount[poolInfo.mintB.address] = quoteTokenAccount;\n\n if (farmInfo !== undefined && !userFarmLpAmount?.isZero()) {\n const farmVersion = FARM_PROGRAM_TO_VERSION[farmInfo.programId];\n const ledger = getAssociatedLedgerAccount({\n programId: new PublicKey(farmInfo.programId),\n poolId: new PublicKey(farmInfo.id),\n owner: this.scope.ownerPubKey,\n version: farmVersion,\n });\n let ledgerInfo: FarmLedger | undefined = undefined;\n const ledgerData = await this.scope.connection.getAccountInfo(ledger);\n if (ledgerData) {\n const ledgerLayout = getFarmLedgerLayout(farmVersion)!;\n ledgerInfo = ledgerLayout.decode(ledgerData.data);\n }\n if (farmVersion !== 6 && !ledgerInfo) {\n const { instruction, instructionType } = createAssociatedLedgerAccountInstruction({\n id: new PublicKey(farmInfo.id),\n programId: new PublicKey(farmInfo.programId),\n version: farmVersion,\n ledger,\n owner: this.scope.ownerPubKey,\n });\n txBuilder.addInstruction({ instructions: [instruction], instructionTypes: [instructionType] });\n }\n\n const rewardTokenAccounts: PublicKey[] = [];\n for (const item of farmInfo.rewardInfos) {\n const rewardIsWsol = item.mint.address === Token.WSOL.mint.toString();\n if (mintToAccount[item.mint.address]) rewardTokenAccounts.push(mintToAccount[item.mint.address]);\n else {\n const { account: farmRewardAccount, instructionParams: ownerTokenAccountFarmInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n mint: new PublicKey(item.mint.address),\n tokenProgram,\n owner: this.scope.ownerPubKey,\n skipCloseAccount: !rewardIsWsol,\n createInfo: {\n payer: payer || this.scope.ownerPubKey,\n },\n associatedOnly: true,\n checkCreateATAOwner,\n });\n if (!farmRewardAccount) this.logAndCreateError(\"farm reward account not found:\", item.mint.address);\n ownerTokenAccountFarmInstruction && txBuilder.addInstruction(ownerTokenAccountFarmInstruction);\n rewardTokenAccounts.push(farmRewardAccount!);\n }\n }\n const farmKeys = (await this.scope.api.fetchFarmKeysById({ ids: farmInfo.id }))[0];\n const insParams = {\n userAuxiliaryLedgers,\n amount: userFarmLpAmount!,\n owner: this.scope.ownerPubKey,\n farmInfo,\n farmKeys,\n lpAccount: lpTokenAccount,\n rewardAccounts: rewardTokenAccounts,\n };\n const version = FARM_PROGRAM_TO_VERSION[farmInfo.programId];\n const newInstruction =\n version === 6\n ? makeWithdrawInstructionV6(insParams)\n : version === 5\n ? makeWithdrawInstructionV5(insParams)\n : makeWithdrawInstructionV3(insParams);\n const insType = {\n 3: InstructionType.FarmV3Withdraw,\n 5: InstructionType.FarmV5Withdraw,\n 6: InstructionType.FarmV6Withdraw,\n };\n txBuilder.addInstruction({\n instructions: [newInstruction],\n instructionTypes: [insType[version]],\n });\n }\n\n const poolKeys = await this.getAmmPoolKeys(poolInfo.id);\n\n const removeIns = removeLiquidityInstruction({\n poolInfo,\n poolKeys,\n userKeys: {\n lpTokenAccount,\n baseTokenAccount,\n quoteTokenAccount,\n owner: this.scope.ownerPubKey,\n },\n lpAmount: amountIn,\n baseAmountMin: 0,\n quoteAmountMin: 0,\n });\n\n txBuilder.addInstruction({\n instructions: [removeIns],\n instructionTypes: [\n !poolInfo.pooltype.includes(\"StablePool\")\n ? InstructionType.AmmV4RemoveLiquidity\n : InstructionType.AmmV5RemoveLiquidity,\n ],\n lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],\n });\n\n const [tokenAccountA, tokenAccountB] =\n poolInfo.mintA.address === clmmPoolInfo.mintA.address\n ? [baseTokenAccount, quoteTokenAccount]\n : [quoteTokenAccount, baseTokenAccount];\n\n const clmmPoolKeys = await this.scope.clmm.getClmmPoolKeys(clmmPoolInfo.id);\n\n const createPositionIns = await ClmmInstrument.openPositionFromBaseInstructions({\n poolInfo: clmmPoolInfo,\n poolKeys: clmmPoolKeys,\n ownerInfo: {\n feePayer: this.scope.ownerPubKey,\n wallet: this.scope.ownerPubKey,\n tokenAccountA,\n tokenAccountB,\n },\n withMetadata: \"create\",\n ...createPositionInfo,\n base,\n getEphemeralSigners,\n });\n\n txBuilder.addInstruction({\n instructions: [...createPositionIns.instructions],\n signers: createPositionIns.signers,\n instructionTypes: [...createPositionIns.instructionTypes],\n lookupTableAddress: clmmPoolKeys.lookupTableAccount ? [clmmPoolKeys.lookupTableAccount] : [],\n });\n\n if (txVersion === TxVersion.V0) return txBuilder.sizeCheckBuildV0() as Promise<MakeMultiTxData<T>>;\n return txBuilder.sizeCheckBuild() as Promise<MakeMultiTxData<T>>;\n }\n\n public async createPoolV4<T extends TxVersion>({\n programId,\n marketInfo,\n baseMintInfo,\n quoteMintInfo,\n baseAmount,\n quoteAmount,\n startTime,\n ownerInfo,\n associatedOnly = false,\n checkCreateATAOwner = false,\n tokenProgram,\n txVersion,\n feeDestinationId,\n computeBudgetConfig,\n }: CreatePoolParam<T>): Promise<MakeTxData<T, { address: CreatePoolAddress }>> {\n const payer = ownerInfo.feePayer || this.scope.owner?.publicKey;\n const mintAUseSOLBalance = ownerInfo.useSOLBalance && baseMintInfo.mint.equals(NATIVE_MINT);\n const mintBUseSOLBalance = ownerInfo.useSOLBalance && quoteMintInfo.mint.equals(NATIVE_MINT);\n\n const txBuilder = this.createTxBuilder();\n\n const { account: ownerTokenAccountBase, instructionParams: ownerTokenAccountBaseInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n mint: baseMintInfo.mint,\n owner: this.scope.ownerPubKey,\n createInfo: mintAUseSOLBalance\n ? {\n payer: payer!,\n amount: baseAmount,\n }\n : undefined,\n notUseTokenAccount: mintAUseSOLBalance,\n skipCloseAccount: !mintAUseSOLBalance,\n associatedOnly: mintAUseSOLBalance ? false : associatedOnly,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(ownerTokenAccountBaseInstruction || {});\n\n const { account: ownerTokenAccountQuote, instructionParams: ownerTokenAccountQuoteInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n mint: quoteMintInfo.mint,\n owner: this.scope.ownerPubKey,\n createInfo: mintBUseSOLBalance\n ? {\n payer: payer!,\n amount: quoteAmount,\n }\n : undefined,\n\n notUseTokenAccount: mintBUseSOLBalance,\n skipCloseAccount: !mintBUseSOLBalance,\n associatedOnly: mintBUseSOLBalance ? false : associatedOnly,\n checkCreateATAOwner,\n });\n txBuilder.addInstruction(ownerTokenAccountQuoteInstruction || {});\n\n if (ownerTokenAccountBase === undefined || ownerTokenAccountQuote === undefined)\n throw Error(\"you don't has some token account\");\n\n const poolInfo = getAssociatedPoolKeys({\n version: 4,\n marketVersion: 3,\n marketId: marketInfo.marketId,\n baseMint: baseMintInfo.mint,\n quoteMint: quoteMintInfo.mint,\n baseDecimals: baseMintInfo.decimals,\n quoteDecimals: quoteMintInfo.decimals,\n programId,\n marketProgramId: marketInfo.programId,\n });\n\n const createPoolKeys = {\n programId,\n ammId: poolInfo.id,\n ammAuthority: poolInfo.authority,\n ammOpenOrders: poolInfo.openOrders,\n lpMint: poolInfo.lpMint,\n coinMint: poolInfo.baseMint,\n pcMint: poolInfo.quoteMint,\n coinVault: poolInfo.baseVault,\n pcVault: poolInfo.quoteVault,\n withdrawQueue: poolInfo.withdrawQueue,\n ammTargetOrders: poolInfo.targetOrders,\n poolTempLp: poolInfo.lpVault,\n marketProgramId: poolInfo.marketProgramId,\n marketId: poolInfo.marketId,\n ammConfigId: poolInfo.configId,\n feeDestinationId,\n };\n\n const { instruction, instructionType } = createPoolV4InstructionV2({\n ...createPoolKeys,\n userWallet: this.scope.ownerPubKey,\n userCoinVault: ownerTokenAccountBase,\n userPcVault: ownerTokenAccountQuote,\n userLpVault: getATAAddress(this.scope.ownerPubKey, poolInfo.lpMint, tokenProgram).publicKey,\n\n nonce: poolInfo.nonce,\n openTime: startTime,\n coinAmount: baseAmount,\n pcAmount: quoteAmount,\n });\n\n txBuilder.addInstruction({\n instructions: [instruction],\n instructionTypes: [instructionType],\n });\n\n txBuilder.addCustomComputeBudget(computeBudgetConfig);\n\n return txBuilder.versionBuild({\n txVersion,\n extInfo: {\n address: createPoolKeys,\n },\n }) as Promise<MakeTxData<T, { address: CreatePoolAddress }>>;\n }\n\n public async getCreatePoolFee({ programId }: { programId: PublicKey }): Promise<BN> {\n const configId = getAssociatedConfigId({ programId });\n\n const account = await this.scope.connection.getAccountInfo(configId, { dataSlice: { offset: 536, length: 8 } });\n if (account === null) throw Error(\"get config account error\");\n\n return createPoolFeeLayout.decode(account.data).fee;\n }\n\n public computeAmountOut({\n poolInfo,\n amountIn,\n mintIn: propMintIn,\n mintOut: propMintOut,\n slippage,\n }: ComputeAmountOutParam): {\n amountOut: BN;\n minAmountOut: BN;\n currentPrice: Decimal;\n executionPrice: Decimal;\n priceImpact: Decimal;\n fee: BN;\n } {\n const [mintIn, mintOut] = [propMintIn.toString(), propMintOut.toString()];\n if (mintIn !== poolInfo.mintA.address && mintIn !== poolInfo.mintB.address) throw new Error(\"toke not match\");\n if (mintOut !== poolInfo.mintA.address && mintOut !== poolInfo.mintB.address) throw new Error(\"toke not match\");\n\n const { baseReserve, quoteReserve } = poolInfo;\n\n const reserves = [baseReserve, quoteReserve];\n const mintDecimals = [poolInfo.mintA.decimals, poolInfo.mintB.decimals];\n\n // input is fixed\n const input = mintIn == poolInfo.mintA.address ? \"base\" : \"quote\";\n if (input === \"quote\") {\n reserves.reverse();\n mintDecimals.reverse();\n }\n\n const [reserveIn, reserveOut] = reserves;\n const [mintInDecimals, mintOutDecimals] = mintDecimals;\n const isVersion4 = poolInfo.version === 4;\n let currentPrice: Decimal;\n if (isVersion4) {\n currentPrice = new Decimal(reserveOut.toString())\n .div(10 ** mintOutDecimals)\n .div(new Decimal(reserveIn.toString()).div(10 ** mintInDecimals));\n } else {\n const p = getStablePrice(\n this.stableLayout.stableModelData,\n baseReserve.toNumber(),\n quoteReserve.toNumber(),\n false,\n );\n if (input === \"quote\") currentPrice = new Decimal(1e6).div(p * 1e6);\n else currentPrice = new Decimal(p * 1e6).div(1e6);\n }\n\n const amountInRaw = amountIn;\n let amountOutRaw = new BN(0);\n let feeRaw = new BN(0);\n\n if (!amountInRaw.isZero()) {\n if (isVersion4) {\n feeRaw = BNDivCeil(amountInRaw.mul(LIQUIDITY_FEES_NUMERATOR), LIQUIDITY_FEES_DENOMINATOR);\n const amountInWithFee = amountInRaw.sub(feeRaw);\n\n const denominator = reserveIn.add(amountInWithFee);\n amountOutRaw = reserveOut.mul(amountInWithFee).div(denominator);\n } else {\n feeRaw = amountInRaw.mul(new BN(2)).div(new BN(10000));\n const amountInWithFee = amountInRaw.sub(feeRaw);\n if (input === \"quote\")\n amountOutRaw = new BN(\n getDyByDxBaseIn(\n this.stableLayout.stableModelData,\n quoteReserve.toNumber(),\n baseReserve.toNumber(),\n amountInWithFee.toNumber(),\n ),\n );\n else {\n amountOutRaw = new BN(\n getDxByDyBaseIn(\n this.stableLayout.stableModelData,\n quoteReserve.toNumber(),\n baseReserve.toNumber(),\n amountInWithFee.toNumber(),\n ),\n );\n }\n }\n }\n\n const minAmountOutRaw = new BN(new Decimal(amountOutRaw.toString()).mul(1 - slippage).toFixed(0));\n\n const amountOut = amountOutRaw;\n const minAmountOut = minAmountOutRaw;\n\n let executionPrice = new Decimal(amountOutRaw.toString()).div(\n new Decimal(amountInRaw.sub(feeRaw).toString()).toFixed(0),\n );\n if (!amountInRaw.isZero() && !amountOutRaw.isZero()) {\n executionPrice = new Decimal(amountOutRaw.toString())\n .div(10 ** mintOutDecimals)\n .div(new Decimal(amountInRaw.sub(feeRaw).toString()).div(10 ** mintInDecimals));\n }\n\n const priceImpact = currentPrice.sub(executionPrice).div(currentPrice).mul(100);\n\n const fee = feeRaw;\n\n return {\n amountOut,\n minAmountOut,\n currentPrice,\n executionPrice,\n priceImpact,\n fee,\n };\n }\n\n public computeAmountIn({ poolInfo, amountOut, mintIn, mintOut, slippage }: ComputeAmountInParam): {\n amountIn: BN;\n maxAmountIn: BN;\n currentPrice: Decimal;\n executionPrice: Decimal | null;\n priceImpact: Decimal;\n } {\n const { baseReserve, quoteReserve } = poolInfo;\n if (mintIn.toString() !== poolInfo.mintA.address && mintIn.toString() !== poolInfo.mintB.address)\n this.logAndCreateError(\"mintIn does not match pool\");\n if (mintOut.toString() !== poolInfo.mintA.address && mintOut.toString() !== poolInfo.mintB.address)\n this.logAndCreateError(\"mintOut does not match pool\");\n this.logDebug(\"baseReserve:\", baseReserve.toString());\n this.logDebug(\"quoteReserve:\", quoteReserve.toString());\n\n const baseIn = mintIn.toString() === poolInfo.mintA.address;\n const [tokenIn, tokenOut] = baseIn ? [poolInfo.mintA, poolInfo.mintB] : [poolInfo.mintB, poolInfo.mintA];\n\n this.logDebug(\"currencyOut:\", tokenOut.symbol || tokenOut.address);\n this.logDebug(\n \"amountOut:\",\n new Decimal(amountOut.toString())\n .div(10 ** tokenOut.decimals)\n .toDecimalPlaces(tokenOut.decimals)\n .toString(),\n tokenIn.symbol || tokenIn.address,\n );\n this.logDebug(\"slippage:\", `${slippage * 100}%`);\n\n const reserves = [baseReserve, quoteReserve];\n\n // output is fixed\n const output = !baseIn ? \"base\" : \"quote\";\n if (output === \"base\") {\n reserves.reverse();\n }\n this.logDebug(\"output side:\", output);\n\n const [reserveIn, reserveOut] = reserves;\n\n const currentPrice = new Decimal(reserveOut.toString())\n .div(10 ** poolInfo[baseIn ? \"mintB\" : \"mintA\"].decimals)\n .div(new Decimal(reserveIn.toString()).div(10 ** poolInfo[baseIn ? \"mintA\" : \"mintB\"].decimals));\n this.logDebug(\n \"currentPrice:\",\n `1 ${tokenIn.symbol || tokenIn.address} ≈ ${currentPrice.toString()} ${tokenOut.symbol || tokenOut.address}`,\n );\n this.logDebug(\n \"currentPrice invert:\",\n `1 ${tokenOut.symbol || tokenOut.address} ≈ ${new Decimal(1).div(currentPrice).toString()} ${\n tokenIn.symbol || tokenIn.address\n }`,\n );\n\n let amountInRaw = new BN(0);\n let amountOutRaw = amountOut;\n if (!amountOutRaw.isZero()) {\n // if out > reserve, out = reserve - 1\n if (amountOutRaw.gt(reserveOut)) {\n amountOutRaw = reserveOut.sub(new BN(1));\n }\n\n const denominator = reserveOut.sub(amountOutRaw);\n const amountInWithoutFee = reserveIn.mul(amountOutRaw).div(denominator);\n\n amountInRaw = amountInWithoutFee\n .mul(LIQUIDITY_FEES_DENOMINATOR)\n .div(LIQUIDITY_FEES_DENOMINATOR.sub(LIQUIDITY_FEES_NUMERATOR));\n }\n\n const maxAmountInRaw = new BN(new Decimal(amountInRaw.toString()).mul(1 + slippage).toFixed(0));\n\n const amountIn = amountInRaw;\n const maxAmountIn = maxAmountInRaw;\n this.logDebug(\n \"amountIn:\",\n new Decimal(amountIn.toString())\n .div(10 ** tokenIn.decimals)\n .toDecimalPlaces(tokenIn.decimals)\n .toString(),\n );\n this.logDebug(\n \"maxAmountIn:\",\n new Decimal(maxAmountIn.toString())\n .div(10 ** tokenIn.decimals)\n .toDecimalPlaces(tokenIn.decimals)\n .toString(),\n );\n\n let executionPrice: Decimal | null = null;\n if (!amountInRaw.isZero() && !amountOutRaw.isZero()) {\n executionPrice = new Decimal(amountOutRaw.toString())\n .div(10 ** tokenOut.decimals)\n .div(new Decimal(amountInRaw.toString()).div(10 ** tokenIn.decimals));\n this.logDebug(\n \"executionPrice:\",\n `1 ${tokenOut.symbol || tokenOut.address} ≈ ${executionPrice\n .toDecimalPlaces(Math.max(poolInfo.mintA.decimals, poolInfo.mintB.decimals))\n .toString()} ${tokenIn.symbol || tokenIn.address}`,\n );\n this.logDebug(\n \"executionPrice invert:\",\n `1 ${tokenOut.symbol || tokenOut.address} ≈ ${new Decimal(1)\n .div(executionPrice)\n .toDecimalPlaces(Math.max(poolInfo.mintA.decimals, poolInfo.mintB.decimals))\n .toString()} ${tokenIn.symbol || tokenIn.address}`,\n );\n }\n\n const exactQuote = currentPrice.mul(amountIn.toString());\n const priceImpact = exactQuote.sub(amountOut.toString()).abs().div(exactQuote);\n this.logDebug(\"priceImpact:\", `${priceImpact.toString()}%`);\n\n return {\n amountIn,\n maxAmountIn,\n currentPrice,\n executionPrice,\n priceImpact,\n };\n }\n\n public async swap<T extends TxVersion>({\n poolInfo,\n poolKeys: propPoolKeys,\n amountIn,\n amountOut,\n inputMint,\n fixedSide,\n txVersion,\n config,\n computeBudgetConfig,\n }: SwapParam<T>): Promise<MakeTxData<T>> {\n const txBuilder = this.createTxBuilder();\n const { associatedOnly = true, inputUseSolBalance = true, outputUseSolBalance = true } = config || {};\n\n const [tokenIn, tokenOut] =\n inputMint === poolInfo.mintA.address ? [poolInfo.mintA, poolInfo.mintB] : [poolInfo.mintB, poolInfo.mintA];\n\n const inputTokenUseSolBalance = inputUseSolBalance && tokenIn.address === WSOLMint.toBase58();\n const outputTokenUseSolBalance = outputUseSolBalance && tokenOut.address === WSOLMint.toBase58();\n\n const { account: _tokenAccountIn, instructionParams: ownerTokenAccountBaseInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n tokenProgram: TOKEN_PROGRAM_ID,\n mint: new PublicKey(tokenIn.address),\n owner: this.scope.ownerPubKey,\n\n createInfo: inputTokenUseSolBalance\n ? {\n payer: this.scope.ownerPubKey,\n amount: amountIn,\n }\n : undefined,\n skipCloseAccount: !inputTokenUseSolBalance,\n notUseTokenAccount: inputTokenUseSolBalance,\n associatedOnly,\n });\n txBuilder.addInstruction(ownerTokenAccountBaseInstruction || {});\n\n if (!_tokenAccountIn)\n this.logAndCreateError(\"input token account not found\", {\n token: tokenIn.symbol || tokenIn.address,\n tokenAccountIn: _tokenAccountIn,\n inputTokenUseSolBalance,\n associatedOnly,\n });\n\n const { account: _tokenAccountOut, instructionParams: ownerTokenAccountQuoteInstruction } =\n await this.scope.account.getOrCreateTokenAccount({\n tokenProgram: TOKEN_PROGRAM_ID,\n mint: new PublicKey(tokenOut.address),\n owner: this.scope.ownerPubKey,\n createInfo: {\n payer: this.scope.ownerPubKey!,\n amount: 0,\n },\n skipCloseAccount: !outputTokenUseSolBalance,\n notUseTokenAccount: outputTokenUseSolBalance,\n associatedOnly: outputTokenUseSolBalance ? false : associatedOnly,\n });\n txBuilder.addInstruction(ownerTokenAccountQuoteInstruction || {});\n if (_tokenAccountOut === undefined)\n this.logAndCreateError(\"output token account not found\", {\n token: tokenOut.symbol || tokenOut.address,\n tokenAccountOut: _tokenAccountOut,\n outputTokenUseSolBalance,\n associatedOnly,\n });\n\n const poolKeys = propPoolKeys || (await this.getAmmPoolKeys(poolInfo.id));\n let version = 4;\n if (poolInfo.pooltype.includes(\"StablePool\")) version = 5;\n\n txBuilder.addInstruction({\n instructions: [\n makeAMMSwapInstruction({\n version,\n poolKeys,\n userKeys: {\n tokenAccountIn: _tokenAccountIn!,\n tokenAccountOut: _tokenAccountOut!,\n owner: this.scope.ownerPubKey,\n },\n amountIn,\n amountOut,\n fixedSide,\n }),\n ],\n instructionTypes: [version === 4 ? InstructionType.AmmV4SwapBaseIn : InstructionType.AmmV5SwapBaseIn],\n });\n\n txBuilder.addCustomComputeBudget(computeBudgetConfig);\n\n return txBuilder.versionBuild({\n txVersion,\n }) as Promise<MakeTxData<T>>;\n }\n\n public async getRpcPoolInfo(poolId: string): Promise<AmmRpcData> {\n return (await this.getRpcPoolInfos([poolId]))[poolId];\n }\n\n public async getRpcPoolInfos(\n poolIds: (string | PublicKey)[],\n config?: { batchRequest?: boolean; chunkCount?: number },\n ): Promise<{\n [poolId: string]: AmmRpcData;\n }> {\n const accounts = await getMultipleAccountsInfoWithCustomFlags(\n this.scope.connection,\n poolIds.map((i) => ({ pubkey: new PublicKey(i) })),\n config,\n );\n const poolInfos: { [poolId: string]: ReturnType<typeof liquidityStateV4Layout.decode> & { programId: PublicKey } } =\n {};\n\n const needFetchVaults: PublicKey[] = [];\n\n for (let i = 0; i < poolIds.length; i++) {\n const item = accounts[i];\n if (item === null || !item.accountInfo) throw Error(\"fetch pool info error: \" + String(poolIds[i]));\n const rpc = liquidityStateV4Layout.decode(item.accountInfo.data);\n poolInfos[String(poolIds[i])] = {\n ...rpc,\n programId: item.accountInfo.owner,\n };\n\n needFetchVaults.push(rpc.baseVault, rpc.quoteVault);\n }\n\n const vaultInfo: { [vaultId: string]: BN } = {};\n const vaultAccountInfo = await getMultipleAccountsInfoWithCustomFlags(\n this.scope.connection,\n needFetchVaults.map((i) => ({ pubkey: new PublicKey(i) })),\n config,\n );\n\n for (let i = 0; i < needFetchVaults.length; i++) {\n const vaultItemInfo = vaultAccountInfo[i].accountInfo;\n if (vaultItemInfo === null) throw Error(\"fetch vault info error: \" + needFetchVaults[i]);\n\n vaultInfo[String(needFetchVaults[i])] = new BN(AccountLayout.decode(vaultItemInfo.data).amount.toString());\n }\n\n const returnData: { [poolId: string]: AmmRpcData } = {};\n\n for (const [id, info] of Object.entries(poolInfos)) {\n const baseReserve = vaultInfo[info.baseVault.toString()].sub(info.baseNeedTakePnl);\n const quoteReserve = vaultInfo[info.quoteVault.toString()].sub(info.quoteNeedTakePnl);\n returnData[id] = {\n ...info,\n baseReserve,\n mintAAmount: vaultInfo[info.baseVault.toString()],\n mintBAmount: vaultInfo[info.quoteVault.toString()],\n quoteReserve,\n poolPrice: new Decimal(quoteReserve.toString())\n .div(new Decimal(10).pow(info.quoteDecimal.toString()))\n .div(new Decimal(baseReserve.toString()).div(new Decimal(10).pow(info.baseDecimal.toString()))),\n };\n }\n\n return returnData;\n }\n\n public async getPoolInfoFromRpc({ poolId }: { poolId: string }): Promise<{\n poolRpcData: AmmRpcData;\n poolInfo: ComputeAmountOutParam[\"poolInfo\"];\n poolKeys: AmmV4Keys | AmmV5Keys;\n }> {\n const rpcData = await this.getRpcPoolInfo(poolId);\n const computeData = toAmmComputePoolInfo({ [poolId]: rpcData });\n const poolInfo = computeData[poolId];\n const allKeys = await this.scope.tradeV2.computePoolToPoolKeys({\n pools: [computeData[poolId]],\n ammRpcData: { [poolId]: rpcData },\n });\n return {\n poolRpcData: rpcData,\n poolInfo,\n poolKeys: allKeys[0] as AmmV4Keys | AmmV5Keys,\n };\n }\n}\n","import { AccountInfo, Commitment, Connection, PublicKey } from \"@solana/web3.js\";\nimport { ReturnTypeFetchMultipleMintInfos } from \"../raydium/type\";\nimport { WSOLMint, chunkArray, solToWSol } from \"./\";\nimport { createLogger } from \"./logger\";\nimport { MINT_SIZE, TOKEN_PROGRAM_ID, getTransferFeeConfig, unpackMint } from \"@solana/spl-token\";\n\ninterface MultipleAccountsJsonRpcResponse {\n jsonrpc: string;\n id: string;\n error?: {\n code: number;\n message: string;\n };\n result: {\n context: { slot: number };\n value: { data: Array<string>; executable: boolean; lamports: number; owner: string; rentEpoch: number }[];\n };\n}\n\nexport interface GetMultipleAccountsInfoConfig {\n batchRequest?: boolean;\n commitment?: Commitment;\n chunkCount?: number;\n}\n\nconst logger = createLogger(\"Raydium_accountInfo_util\");\n\nexport async function getMultipleAccountsInfo(\n connection: Connection,\n publicKeys: PublicKey[],\n config?: GetMultipleAccountsInfoConfig,\n): Promise<(AccountInfo<Buffer> | null)[]> {\n const {\n batchRequest,\n commitment = \"confirmed\",\n chunkCount = 100,\n } = {\n batchRequest: false,\n ...config,\n };\n\n const chunkedKeys = chunkArray(publicKeys, chunkCount);\n let results: (AccountInfo<Buffer> | null)[][] = new Array(chunkedKeys.length).fill([]);\n\n if (batchRequest) {\n const batch = chunkedKeys.map((keys) => {\n const args = connection._buildArgs([keys.map((key) => key.toBase58())], commitment, \"base64\");\n return {\n methodName: \"getMultipleAccounts\",\n args,\n };\n });\n\n const _batch = chunkArray(batch, 10);\n\n const unsafeResponse: MultipleAccountsJsonRpcResponse[] = await (\n await Promise.all(_batch.map(async (i) => await (connection as any)._rpcBatchRequest(i)))\n ).flat();\n results = unsafeResponse.map((unsafeRes: MultipleAccountsJsonRpcResponse) => {\n if (unsafeRes.error)\n logger.logWithError(`failed to get info for multiple accounts, RPC_ERROR, ${unsafeRes.error.message}`);\n\n return unsafeRes.result.value.map((accountInfo) => {\n if (accountInfo) {\n const { data, executable, lamports, owner, rentEpoch } = accountInfo;\n\n if (data.length !== 2 && data[1] !== \"base64\") logger.logWithError(`info must be base64 encoded, RPC_ERROR`);\n\n return {\n data: Buffer.from(data[0], \"base64\"),\n executable,\n lamports,\n owner: new PublicKey(owner),\n rentEpoch,\n };\n }\n return null;\n });\n });\n } else {\n try {\n results = (await Promise.all(\n chunkedKeys.map((keys) => connection.getMultipleAccountsInfo(keys, commitment)),\n )) as (AccountInfo<Buffer> | null)[][];\n } catch (error) {\n if (error instanceof Error) {\n logger.logWithError(`failed to get info for multiple accounts, RPC_ERROR, ${error.message}`);\n }\n }\n }\n\n return results.flat();\n}\n\nexport async function getMultipleAccountsInfoWithCustomFlags<T extends { pubkey: PublicKey }>(\n connection: Connection,\n publicKeysWithCustomFlag: T[],\n config?: GetMultipleAccountsInfoConfig,\n): Promise<({ accountInfo: AccountInfo<Buffer> | null } & T)[]> {\n const multipleAccountsInfo = await getMultipleAccountsInfo(\n connection,\n publicKeysWithCustomFlag.map((o) => o.pubkey),\n config,\n );\n\n return publicKeysWithCustomFlag.map((o, idx) => ({ ...o, accountInfo: multipleAccountsInfo[idx] }));\n}\n\nexport enum AccountType {\n Uninitialized,\n Mint,\n Account,\n}\nexport const ACCOUNT_TYPE_SIZE = 1;\n\nexport async function fetchMultipleMintInfos({\n connection,\n mints,\n config,\n}: {\n connection: Connection;\n mints: PublicKey[];\n config?: { batchRequest?: boolean };\n}): Promise<ReturnTypeFetchMultipleMintInfos> {\n if (mints.length === 0) return {};\n const mintInfos = await getMultipleAccountsInfoWithCustomFlags(\n connection,\n mints.map((i) => ({ pubkey: solToWSol(i) })),\n config,\n );\n\n const mintK: ReturnTypeFetchMultipleMintInfos = {};\n for (const i of mintInfos) {\n if (!i.accountInfo || i.accountInfo.data.length < MINT_SIZE) {\n console.log(\"invalid mint account\", i.pubkey.toBase58());\n continue;\n }\n const t = unpackMint(i.pubkey, i.accountInfo, i.accountInfo?.owner);\n mintK[i.pubkey.toString()] = {\n ...t,\n programId: i.accountInfo?.owner || TOKEN_PROGRAM_ID,\n feeConfig: getTransferFeeConfig(t) ?? undefined,\n };\n }\n mintK[PublicKey.default.toBase58()] = mintK[WSOLMint.toBase58()];\n\n return mintK;\n}\n","import BN from \"bn.js\";\nimport Decimal from \"decimal.js\";\nimport { CurrencyAmount, TokenAmount } from \"../module/amount\";\nimport { Currency } from \"../module/currency\";\nimport { Fraction } from \"../module/fraction\";\nimport { Percent } from \"../module/percent\";\nimport { Price } from \"../module/price\";\nimport { Token } from \"../module/token\";\nimport { SplToken, TokenJson } from \"../raydium/token/