test-raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
1 lines • 775 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/raydium/tradeV2/trade.ts","../../../src/common/logger.ts","../../../src/common/utility.ts","../../../src/module/amount.ts","../../../src/common/bignumber.ts","../../../node_modules/decimal.js/decimal.mjs","../../../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/txType.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/moduleBase.ts","../../../src/raydium/tradeV2/instrument.ts","../../../src/raydium/clmm/clmm.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/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","../../../src/raydium/clmm/instrument.ts","../../../src/raydium/account/util.ts","../../../src/raydium/account/layout.ts","../../../node_modules/@noble/hashes/src/_assert.ts","../../../node_modules/@noble/hashes/src/utils.ts","../../../node_modules/@noble/hashes/src/_md.ts","../../../node_modules/@noble/hashes/src/sha256.ts","../../../src/raydium/liquidity/instruction.ts","../../../src/raydium/liquidity/layout.ts","../../../src/raydium/liquidity/stable.ts","../../../src/raydium/account/instruction.ts"],"sourcesContent":["import { PublicKey, TransactionInstruction } from \"@solana/web3.js\";\nimport { createTransferInstruction } from \"@solana/spl-token\";\nimport { forecastTransactionSize, solToWSol, TxBuilder, BN_ZERO, SOLMint, WSOLMint, addComputeBudget } from \"@/common\";\nimport { Token } from \"@/module\";\nimport { StableLayout } from \"../liquidity/stable\";\nimport ModuleBase, { ModuleBaseProps } from \"../moduleBase\";\nimport {\n ComputeAmountOutLayout,\n ComputeAmountOutRouteLayout,\n PoolAccountInfoV4,\n ReturnTypeGetAddLiquidityDefaultPool,\n} from \"./type\";\nimport { makeSwapInstruction } from \"./instrument\";\nimport { MakeMultiTransaction, MakeTransaction } from \"../type\";\nimport { InstructionType } from \"@/common/txTool/txType\";\nimport { BigNumberish, parseBigNumberish } from \"@/common/bignumber\";\nimport {\n createWSolAccountInstructions,\n closeAccountInstruction,\n makeTransferInstruction,\n} from \"../account/instruction\";\nimport { TokenAccount } from \"../account/types\";\nimport { ComputeBudgetConfig } from \"@/raydium/type\";\n\ntype LiquidityPoolJsonInfo = any;\nexport default class TradeV2 extends ModuleBase {\n constructor(params: ModuleBaseProps) {\n super(params);\n }\n\n static getAddLiquidityDefaultPool({\n addLiquidityPools,\n poolInfosCache,\n }: {\n addLiquidityPools: LiquidityPoolJsonInfo[];\n poolInfosCache: { [ammId: string]: PoolAccountInfoV4 };\n }): ReturnTypeGetAddLiquidityDefaultPool {\n if (addLiquidityPools.length === 0) return undefined;\n if (addLiquidityPools.length === 1) return addLiquidityPools[0];\n addLiquidityPools.sort((a, b) => b.version - a.version);\n if (addLiquidityPools[0].version !== addLiquidityPools[1].version) return addLiquidityPools[0];\n\n const _addLiquidityPools = addLiquidityPools.filter((i) => i.version === addLiquidityPools[0].version);\n\n _addLiquidityPools.sort((a, b) => this.comparePoolSize(a, b, poolInfosCache));\n return _addLiquidityPools[0];\n }\n\n private static comparePoolSize(\n a: LiquidityPoolJsonInfo,\n b: LiquidityPoolJsonInfo,\n ammIdToPoolInfo: { [ammId: string]: PoolAccountInfoV4 },\n ): number {\n const aInfo = ammIdToPoolInfo[a.id];\n const bInfo = ammIdToPoolInfo[b.id];\n if (aInfo === undefined) return 1;\n if (bInfo === undefined) return -1;\n\n if (a.baseMint === b.baseMint) {\n const sub = aInfo.baseReserve.sub(bInfo.baseReserve);\n return sub.gte(BN_ZERO) ? -1 : 1;\n } else {\n const sub = aInfo.baseReserve.sub(bInfo.quoteReserve);\n return sub.gte(BN_ZERO) ? -1 : 1;\n }\n }\n\n // public async getAllRouteComputeAmountOut({\n // inputTokenAmount,\n // outputToken: orgOut,\n // directPath,\n // routePathDict,\n // simulateCache,\n // tickCache,\n // slippage,\n // chainTime,\n // feeConfig,\n // mintInfos,\n // }: {\n // directPath: PoolType[];\n // routePathDict: RoutePathType;\n // simulateCache: ReturnTypeFetchMultipleInfo;\n // tickCache: ReturnTypeFetchMultiplePoolTickArrays;\n // inputTokenAmount: TokenAmount;\n // outputToken: Token;\n // slippage: Percent;\n // chainTime: number;\n // feeConfig?: {\n // feeBps: BN;\n // feeAccount: PublicKey;\n // };\n // mintInfos: ReturnTypeFetchMultipleMintInfos;\n // }): Promise<{\n // routes: ComputeAmountOutLayout[];\n // best?: ComputeAmountOutLayout;\n // }> {\n // const epochInfo = await this.scope.fetchEpochInfo();\n // const input = this.scope.solToWsolTokenAmount(inputTokenAmount);\n // const _amountIn =\n // feeConfig === undefined ? BN_ZERO : input.raw.mul(new BN(10000 - feeConfig.feeBps.toNumber())).div(new BN(10000));\n // const amountIn = feeConfig === undefined ? input : new TokenAmount(input.token, _amountIn, true);\n // const _inFeeConfig =\n // feeConfig === undefined\n // ? undefined\n // : {\n // feeAmount: _amountIn,\n // feeAccount: feeConfig.feeAccount,\n // };\n\n // const outputToken = this.scope.mintToToken(solToWSol(orgOut.mint));\n // const outRoute: ComputeAmountOutLayout[] = [];\n\n // for (const itemPool of directPath) {\n // if (itemPool.version === 6) {\n // try {\n // const {\n // realAmountIn,\n // amountOut,\n // minAmountOut,\n // expirationTime,\n // currentPrice,\n // executionPrice,\n // priceImpact,\n // fee,\n // remainingAccounts,\n // } = await PoolUtils.computeAmountOutFormat({\n // poolInfo: itemPool as ClmmPoolInfo,\n // tickArrayCache: tickCache[itemPool.id.toString()],\n // amountIn,\n // tokenOut: outputToken,\n // slippage,\n // token2022Infos: mintInfos,\n // epochInfo,\n // });\n // outRoute.push({\n // amountIn: realAmountIn,\n // amountOut,\n // minAmountOut,\n // currentPrice,\n // executionPrice,\n // priceImpact,\n // fee: [fee],\n // remainingAccounts: [remainingAccounts],\n // routeType: \"amm\",\n // poolKey: [itemPool],\n // poolReady: (itemPool as ClmmPoolInfo).startTime < chainTime,\n // poolType: \"CLMM\",\n // feeConfig: _inFeeConfig,\n // expirationTime: minExpirationTime(realAmountIn.expirationTime, expirationTime),\n // });\n // } catch (e) {\n // //\n // }\n // } else {\n // try {\n // if (![1, 6, 7].includes(simulateCache[itemPool.id as string].status.toNumber())) continue;\n // // const { amountOut, minAmountOut, currentPrice, executionPrice, priceImpact, fee } =\n // // this.scope.liquidity.computeAmountOut({\n // // poolKeys: jsonInfo2PoolKeys(itemPool) as LiquidityPoolKeys,\n // // poolInfo: simulateCache[itemPool.id as string],\n // // amountIn,\n // // outputToken,\n // // slippage,\n // // });\n // // outRoute.push({\n // // amountIn: { amount: amountIn, fee: undefined, expirationTime: undefined },\n // // amountOut: { amount: amountOut, fee: undefined, expirationTime: undefined },\n // // minAmountOut: { amount: minAmountOut, fee: undefined, expirationTime: undefined },\n // // currentPrice,\n // // executionPrice,\n // // priceImpact,\n // // fee: [fee],\n // // routeType: \"amm\",\n // // poolKey: [itemPool],\n // // remainingAccounts: [],\n // // poolReady: simulateCache[itemPool.id as string].startTime.toNumber() < chainTime,\n // // poolType: itemPool.version === 5 ? \"STABLE\" : undefined,\n // // feeConfig: _inFeeConfig,\n // // expirationTime: undefined,\n // // });\n // } catch (e) {\n // //\n // }\n // }\n // }\n // for (const [routeMint, info] of Object.entries(routePathDict)) {\n // for (const iFromPool of info.in) {\n // if (!simulateCache[iFromPool.id as string] && !tickCache[iFromPool.id.toString()]) continue;\n // if (iFromPool.version !== 6 && ![1, 6, 7].includes(simulateCache[iFromPool.id as string].status.toNumber()))\n // continue;\n // for (const iOutPool of info.out) {\n // if (!simulateCache[iOutPool.id as string] && !tickCache[iOutPool.id.toString()]) continue;\n // if (iOutPool.version !== 6 && ![1, 6, 7].includes(simulateCache[iOutPool.id as string].status.toNumber()))\n // continue;\n // try {\n // const {\n // amountOut,\n // minAmountOut,\n // executionPrice,\n // priceImpact,\n // fee,\n // remainingAccounts,\n // minMiddleAmountFee,\n // middleToken,\n // expirationTime,\n // realAmountIn,\n // } = await this.computeAmountOut({\n // middleMintInfo: {\n // mint: new PublicKey(routeMint),\n // decimals: info.mDecimals,\n // },\n // amountIn,\n // currencyOut: outputToken,\n // slippage,\n\n // fromPool: iFromPool,\n // toPool: iOutPool,\n // simulateCache,\n // tickCache,\n // mintInfos,\n // });\n\n // const infoAPoolOpen =\n // iFromPool.version === 6\n // ? (iFromPool as ClmmPoolInfo).startTime < chainTime\n // : simulateCache[iFromPool.id as string].startTime.toNumber() < chainTime;\n // const infoBPoolOpen =\n // iOutPool.version === 6\n // ? (iOutPool as ClmmPoolInfo).startTime < chainTime\n // : simulateCache[iOutPool.id as string].startTime.toNumber() < chainTime;\n\n // const poolTypeA = iFromPool.version === 6 ? \"CLMM\" : iFromPool.version === 5 ? \"STABLE\" : undefined;\n // const poolTypeB = iOutPool.version === 6 ? \"CLMM\" : iOutPool.version === 5 ? \"STABLE\" : undefined;\n // outRoute.push({\n // amountIn: realAmountIn,\n // amountOut,\n // minAmountOut,\n // currentPrice: undefined,\n // executionPrice,\n // priceImpact,\n // fee,\n // routeType: \"route\",\n // poolKey: [iFromPool, iOutPool],\n // remainingAccounts,\n // minMiddleAmountFee,\n // middleToken,\n // poolReady: infoAPoolOpen && infoBPoolOpen,\n // poolType: [poolTypeA, poolTypeB],\n // feeConfig: _inFeeConfig,\n // expirationTime,\n // });\n // } catch (e) {\n // //\n // }\n // }\n // }\n // }\n // outRoute.sort((a, b) => (a.amountOut.amount.raw.sub(b.amountOut.amount.raw).gt(BN_ZERO) ? -1 : 1));\n // const isReadyRoutes = outRoute.filter((i) => i.poolReady);\n\n // return {\n // routes: outRoute,\n // best: isReadyRoutes.length ? isReadyRoutes[0] : outRoute[0],\n // };\n // }\n\n // private async computeAmountOut({\n // middleMintInfo,\n // amountIn,\n // currencyOut,\n // slippage,\n\n // fromPool,\n // toPool,\n // simulateCache,\n // tickCache,\n // mintInfos,\n // }: {\n // middleMintInfo: { mint: PublicKey; decimals: number };\n // amountIn: TokenAmount;\n // currencyOut: Token;\n // slippage: Percent;\n // fromPool: PoolType;\n // toPool: PoolType;\n // simulateCache: ReturnTypeFetchMultipleInfo;\n // tickCache: ReturnTypeFetchMultiplePoolTickArrays;\n // mintInfos: ReturnTypeFetchMultipleMintInfos;\n // }): Promise<{\n // minMiddleAmountFee: TokenAmount | undefined;\n // middleToken: Token;\n // realAmountIn: TransferAmountFee;\n // amountOut: TransferAmountFee;\n // minAmountOut: TransferAmountFee;\n // executionPrice: Price | null;\n // priceImpact: Fraction;\n // fee: TokenAmount[];\n // remainingAccounts: [PublicKey[] | undefined, PublicKey[] | undefined];\n // expirationTime: number | undefined;\n // }> {\n // const epochInfo = await this.scope.fetchEpochInfo();\n // const middleToken = new Token(middleMintInfo);\n\n // let firstPriceImpact: Percent;\n // let firstFee: TokenAmount;\n // let firstRemainingAccounts: PublicKey[] | undefined = undefined;\n // let minMiddleAmountOut: TransferAmountFee;\n // let firstExpirationTime: number | undefined = undefined;\n // let realAmountIn: TransferAmountFee = {\n // amount: amountIn,\n // fee: undefined,\n // expirationTime: undefined,\n // };\n\n // const _slippage = new Percent(0, 100);\n\n // if (fromPool.version === 6) {\n // const {\n // minAmountOut: _minMiddleAmountOut,\n // priceImpact: _firstPriceImpact,\n // fee: _firstFee,\n // remainingAccounts: _firstRemainingAccounts,\n // expirationTime: _expirationTime,\n // realAmountIn: _realAmountIn,\n // } = await PoolUtils.computeAmountOutFormat({\n // poolInfo: fromPool as ClmmPoolInfo,\n // tickArrayCache: tickCache[fromPool.id.toString()],\n // amountIn,\n // tokenOut: middleToken,\n // slippage: _slippage,\n // epochInfo,\n // token2022Infos: mintInfos,\n // });\n // minMiddleAmountOut = _minMiddleAmountOut;\n // firstPriceImpact = _firstPriceImpact;\n // firstFee = _firstFee;\n // firstRemainingAccounts = _firstRemainingAccounts;\n // firstExpirationTime = _expirationTime;\n // realAmountIn = _realAmountIn;\n // } else {\n // const {\n // minAmountOut: _minMiddleAmountOut,\n // priceImpact: _firstPriceImpact,\n // fee: _firstFee,\n // } = this.scope.liquidity.computeAmountOut({\n // poolKeys: jsonInfo2PoolKeys(fromPool) as LiquidityPoolKeys,\n // poolInfo: simulateCache[fromPool.id as string],\n // amountIn,\n // outputToken: middleToken,\n // slippage: _slippage,\n // });\n // minMiddleAmountOut = {\n // amount: _minMiddleAmountOut,\n // fee: undefined,\n // expirationTime: undefined,\n // };\n // firstPriceImpact = _firstPriceImpact;\n // firstFee = _firstFee;\n // }\n\n // let amountOut: TransferAmountFee;\n // let minAmountOut: TransferAmountFee;\n // let secondPriceImpact: Percent;\n // let secondFee: TokenAmount;\n // let secondRemainingAccounts: PublicKey[] | undefined = undefined;\n // let secondExpirationTime: number | undefined = undefined;\n // let realAmountRouteIn: TransferAmountFee = minMiddleAmountOut;\n\n // if (toPool.version === 6) {\n // const {\n // amountOut: _amountOut,\n // minAmountOut: _minAmountOut,\n // priceImpact: _secondPriceImpact,\n // fee: _secondFee,\n // remainingAccounts: _secondRemainingAccounts,\n // expirationTime: _expirationTime,\n // realAmountIn: _realAmountIn,\n // } = await PoolUtils.computeAmountOutFormat({\n // poolInfo: toPool as ClmmPoolInfo,\n // tickArrayCache: tickCache[toPool.id.toString()],\n // amountIn: new TokenAmount(\n // (minMiddleAmountOut.amount as TokenAmount).token,\n // minMiddleAmountOut.amount.raw.sub(\n // minMiddleAmountOut.fee === undefined ? BN_ZERO : minMiddleAmountOut.fee.raw,\n // ),\n // ),\n // tokenOut: currencyOut,\n // slippage,\n // epochInfo,\n // token2022Infos: mintInfos,\n // });\n // amountOut = _amountOut;\n // minAmountOut = _minAmountOut;\n // secondPriceImpact = _secondPriceImpact;\n // secondFee = _secondFee;\n // secondRemainingAccounts = _secondRemainingAccounts;\n // secondExpirationTime = _expirationTime;\n // realAmountRouteIn = _realAmountIn;\n // } else {\n // const {\n // amountOut: _amountOut,\n // minAmountOut: _minAmountOut,\n // priceImpact: _secondPriceImpact,\n // fee: _secondFee,\n // } = this.scope.liquidity.computeAmountOut({\n // poolKeys: jsonInfo2PoolKeys(toPool) as LiquidityPoolKeys,\n // poolInfo: simulateCache[toPool.id as string],\n // amountIn: new TokenAmount(\n // minMiddleAmountOut.amount.token,\n // minMiddleAmountOut.amount.raw.sub(\n // minMiddleAmountOut.fee === undefined ? BN_ZERO : minMiddleAmountOut.fee.raw,\n // ),\n // ),\n // outputToken: currencyOut,\n // slippage,\n // });\n // amountOut = {\n // amount: _amountOut,\n // fee: undefined,\n // expirationTime: undefined,\n // };\n // minAmountOut = {\n // amount: _minAmountOut,\n // fee: undefined,\n // expirationTime: undefined,\n // };\n // secondPriceImpact = _secondPriceImpact;\n // secondFee = _secondFee;\n // }\n\n // let executionPrice: Price | null = null;\n // const amountInRaw = amountIn.raw;\n // const amountOutRaw = amountOut.amount.raw;\n // const currencyIn = amountIn.token;\n // if (!amountInRaw.isZero() && !amountOutRaw.isZero()) {\n // executionPrice = new Price({\n // baseToken: currencyIn,\n // denominator: amountInRaw,\n // quoteToken: currencyOut,\n // numerator: amountOutRaw,\n // });\n // }\n\n // return {\n // minMiddleAmountFee:\n // minMiddleAmountOut.fee !== undefined\n // ? new TokenAmount(\n // middleToken,\n // (minMiddleAmountOut.fee?.raw ?? new BN(0)).add(realAmountRouteIn.fee?.raw ?? new BN(0)),\n // )\n // : undefined,\n // middleToken,\n // realAmountIn,\n // amountOut,\n // minAmountOut,\n // executionPrice,\n // priceImpact: firstPriceImpact.add(secondPriceImpact),\n // fee: [firstFee, secondFee],\n // remainingAccounts: [firstRemainingAccounts, secondRemainingAccounts],\n // expirationTime: minExpirationTime(firstExpirationTime, secondExpirationTime),\n // };\n // }\n\n private async getWSolAccounts(): Promise<TokenAccount[]> {\n this.scope.checkOwner();\n await this.scope.account.fetchWalletTokenAccounts();\n const tokenAccounts = this.scope.account.tokenAccounts.filter((acc) => acc.mint.equals(WSOLMint));\n tokenAccounts.sort((a, b) => {\n if (a.isAssociated) return 1;\n if (b.isAssociated) return -1;\n return a.amount.lt(b.amount) ? -1 : 1;\n });\n return tokenAccounts;\n }\n\n public async unWrapWSol(props: {\n amount: BigNumberish;\n computeBudgetConfig?: ComputeBudgetConfig;\n tokenProgram?: PublicKey;\n }): Promise<MakeTransaction> {\n const { amount, tokenProgram } = props;\n const tokenAccounts = await this.getWSolAccounts();\n const txBuilder = this.createTxBuilder();\n txBuilder.addCustomComputeBudget(props.computeBudgetConfig);\n const ins = await createWSolAccountInstructions({\n connection: this.scope.connection,\n owner: this.scope.ownerPubKey,\n payer: this.scope.ownerPubKey,\n amount: 0,\n });\n txBuilder.addInstruction(ins);\n\n const amountBN = parseBigNumberish(amount);\n for (let i = 0; i < tokenAccounts.length; i++) {\n if (amountBN.gte(tokenAccounts[i].amount)) {\n txBuilder.addInstruction({\n instructions: [\n closeAccountInstruction({\n tokenAccount: tokenAccounts[i].publicKey!,\n payer: this.scope.ownerPubKey,\n owner: this.scope.ownerPubKey,\n programId: tokenProgram,\n }),\n ],\n });\n amountBN.sub(tokenAccounts[i].amount);\n } else {\n txBuilder.addInstruction({\n instructions: [\n closeAccountInstruction({\n tokenAccount: tokenAccounts[i].publicKey!,\n payer: this.scope.ownerPubKey,\n owner: this.scope.ownerPubKey,\n programId: tokenProgram,\n }),\n ],\n });\n makeTransferInstruction({\n destination: ins.addresses.newAccount,\n source: tokenAccounts[i].publicKey!,\n amount: amountBN,\n owner: this.scope.ownerPubKey,\n tokenProgram,\n });\n }\n }\n\n return txBuilder.build();\n }\n\n public async wrapWSol(amount: BigNumberish, tokenProgram?: PublicKey): Promise<MakeTransaction> {\n const tokenAccounts = await this.getWSolAccounts();\n\n const txBuilder = this.createTxBuilder();\n const ins = await createWSolAccountInstructions({\n connection: this.scope.connection,\n owner: this.scope.ownerPubKey,\n payer: this.scope.ownerPubKey,\n amount,\n skipCloseAccount: true,\n });\n txBuilder.addInstruction(ins);\n\n if (tokenAccounts.length) {\n // already have wsol account\n txBuilder.addInstruction({\n instructions: [\n makeTransferInstruction({\n // destination: ins.signers![0].publicKey,\n destination: tokenAccounts[0].publicKey!,\n source: ins.addresses.newAccount,\n amount,\n owner: this.scope.ownerPubKey,\n tokenProgram,\n }),\n ],\n endInstructions: [\n closeAccountInstruction({\n tokenAccount: ins.addresses.newAccount,\n payer: this.scope.ownerPubKey,\n owner: this.scope.ownerPubKey,\n programId: tokenProgram,\n }),\n ],\n });\n }\n return txBuilder.build();\n }\n\n public async swap({\n swapInfo: orgSwapInfo,\n associatedOnly,\n checkCreateATAOwner,\n checkTransaction,\n routeProgram = new PublicKey(\"routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS\"),\n }: {\n swapInfo: ComputeAmountOutLayout;\n associatedOnly: boolean;\n checkCreateATAOwner: boolean;\n checkTransaction: boolean;\n routeProgram: PublicKey;\n }): Promise<MakeMultiTransaction> {\n const swapInfo = {\n ...orgSwapInfo,\n amountIn: this.scope.solToWsolTransferAmountFee(orgSwapInfo.amountIn),\n amountOut: this.scope.solToWsolTransferAmountFee(orgSwapInfo.amountOut),\n minAmountOut: this.scope.solToWsolTransferAmountFee(orgSwapInfo.minAmountOut),\n middleMint: (orgSwapInfo as ComputeAmountOutRouteLayout).minMiddleAmountFee\n ? solToWSol((orgSwapInfo as ComputeAmountOutRouteLayout).middleToken.mint)\n : undefined,\n };\n const amountIn = swapInfo.amountIn;\n const amountOut = swapInfo.amountOut;\n const useSolBalance =\n amountIn.amount.token.mint.equals(Token.WSOL.mint) || amountIn.amount.token.mint.equals(SOLMint);\n const outSolBalance =\n amountOut.amount.token.mint.equals(Token.WSOL.mint) || amountOut.amount.token.mint.equals(SOLMint);\n const inputMint = amountIn.amount.token.mint;\n const middleMint = swapInfo.middleMint!;\n const outputMint = amountOut.amount.token.mint;\n const txBuilder = this.createTxBuilder();\n\n const { account: sourceToken, instructionParams: sourceInstructionParams } =\n await this.scope.account.getOrCreateTokenAccount({\n mint: inputMint,\n notUseTokenAccount: useSolBalance,\n createInfo: useSolBalance\n ? {\n payer: this.scope.ownerPubKey,\n amount: amountIn.amount.raw,\n }\n : undefined,\n owner: this.scope.ownerPubKey,\n skipCloseAccount: !useSolBalance,\n associatedOnly: useSolBalance ? false : associatedOnly,\n checkCreateATAOwner,\n });\n sourceInstructionParams && txBuilder.addInstruction(sourceInstructionParams);\n if (sourceToken === undefined) throw Error(\"input account check error\");\n\n const { account: destinationToken, instructionParams: destinationInstructionParams } =\n await this.scope.account.getOrCreateTokenAccount({\n mint: outputMint,\n skipCloseAccount: !outSolBalance,\n createInfo: {\n payer: this.scope.ownerPubKey,\n amount: 0,\n },\n owner: this.scope.ownerPubKey,\n associatedOnly,\n checkCreateATAOwner,\n });\n destinationInstructionParams && txBuilder.addInstruction(destinationInstructionParams);\n\n let routeToken: PublicKey | undefined = undefined;\n if (swapInfo.routeType === \"route\") {\n const res = await this.scope.account.getOrCreateTokenAccount({\n mint: middleMint,\n createInfo: {\n payer: this.scope.ownerPubKey,\n amount: 0,\n },\n owner: this.scope.ownerPubKey,\n associatedOnly: false,\n checkCreateATAOwner,\n });\n routeToken = res.account;\n res.instructionParams && txBuilder.addInstruction(res.instructionParams);\n }\n\n const ins = await makeSwapInstruction({\n routeProgram,\n inputMint,\n swapInfo,\n ownerInfo: {\n wallet: this.scope.ownerPubKey,\n sourceToken,\n routeToken,\n destinationToken: destinationToken!,\n },\n });\n\n const transferIns =\n swapInfo.feeConfig !== undefined\n ? [\n createTransferInstruction(\n sourceToken,\n swapInfo.feeConfig.feeAccount,\n this.scope.ownerPubKey,\n swapInfo.feeConfig.feeAmount.toNumber(),\n ),\n ]\n : [];\n const transferInsType = swapInfo.feeConfig !== undefined ? [InstructionType.TransferAmount] : [];\n\n const instructions: TransactionInstruction[] = [];\n const instructionsTypes: string[] = [];\n const config = await txBuilder.getComputeBudgetConfig();\n if (config) {\n const { instructions: _ins, instructionTypes: _insType } = addComputeBudget(config);\n instructions.push(..._ins);\n instructionsTypes.push(..._insType);\n }\n\n const allTxBuilder: TxBuilder[] = [];\n const tempIns = [\n ...instructions,\n ...transferIns,\n ...txBuilder.AllTxData.instructions,\n ...ins.instructions,\n ...txBuilder.AllTxData.endInstructions,\n ];\n const tempInsType = [\n ...instructionsTypes,\n ...transferInsType,\n ...txBuilder.AllTxData.instructionTypes,\n ...ins.instructionTypes,\n ...txBuilder.AllTxData.endInstructionTypes,\n ];\n const tempSigner = [...txBuilder.AllTxData.signers, ...ins.signers];\n if (checkTransaction) {\n if (forecastTransactionSize(tempIns, [this.scope.ownerPubKey, ...tempSigner.map((i) => i.publicKey)])) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: tempIns,\n signers: tempSigner,\n instructionTypes: tempInsType,\n }),\n );\n } else {\n if (txBuilder.AllTxData.instructions.length > 0) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: txBuilder.AllTxData.instructions,\n signers: txBuilder.AllTxData.signers,\n instructionTypes: txBuilder.AllTxData.instructionTypes,\n }),\n );\n }\n if (forecastTransactionSize([...instructions, ...transferIns, ...ins.instructions], [this.scope.ownerPubKey])) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: [...instructions, ...transferIns, ...ins.instructions],\n signers: ins.signers,\n instructionTypes: [...instructionsTypes, ...transferInsType, ...ins.instructionTypes],\n }),\n );\n } else if (forecastTransactionSize([...instructions, ...ins.instructions], [this.scope.ownerPubKey])) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: [...instructions, ...ins.instructions],\n signers: ins.signers,\n instructionTypes: ins.instructionTypes,\n }),\n );\n } else if (forecastTransactionSize(ins.instructions, [this.scope.ownerPubKey])) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: [...ins.instructions],\n signers: ins.signers,\n instructionTypes: ins.instructionTypes,\n }),\n );\n } else {\n for (let index = 0; index < ins.instructions.length; index++) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: [...instructions, ins.instructions[index]],\n signers: ins.signers,\n instructionTypes: [...instructionsTypes, ins.instructionTypes[index]],\n }),\n );\n }\n }\n if (txBuilder.AllTxData.endInstructions.length > 0) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: txBuilder.AllTxData.endInstructions,\n instructionTypes: txBuilder.AllTxData.endInstructionTypes,\n }),\n );\n }\n }\n } else {\n if (swapInfo.routeType === \"amm\") {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: tempIns,\n signers: tempSigner,\n instructionTypes: tempInsType,\n }),\n );\n } else {\n if (txBuilder.AllTxData.instructions.length > 0) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: txBuilder.AllTxData.instructions,\n signers: txBuilder.AllTxData.signers,\n instructionTypes: txBuilder.AllTxData.instructionTypes,\n }),\n );\n }\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: ins.instructions,\n signers: ins.signers,\n instructionTypes: ins.instructionTypes,\n }),\n );\n if (txBuilder.AllTxData.endInstructions.length > 0) {\n allTxBuilder.push(\n this.createTxBuilder().addInstruction({\n instructions: txBuilder.AllTxData.endInstructions,\n instructionTypes: txBuilder.AllTxData.endInstructionTypes,\n }),\n );\n }\n }\n }\n const firstBuilder = allTxBuilder.shift()!;\n return firstBuilder.buildMultiTx({ extraPreBuildData: allTxBuilder.map((builder) => builder.build()) });\n }\n}\n","import { get, set } from \"lodash\";\nimport dayjs from \"dayjs\";\nimport utc from \"dayjs/plugin/utc\";\ndayjs.extend(utc);\n\nexport type ModuleName = \"Common.Api\";\n\nexport enum LogLevel {\n Error,\n Warning,\n Info,\n Debug,\n}\nexport class Logger {\n private logLevel: LogLevel;\n private name: string;\n constructor(params: { name: string; logLevel?: LogLevel }) {\n this.logLevel = params.logLevel !== undefined ? params.logLevel : LogLevel.Error;\n this.name = params.name;\n }\n\n set level(logLevel: LogLevel) {\n this.logLevel = logLevel;\n }\n get time(): string {\n return dayjs().utc().format(\"YYYY/MM/DD HH:mm:ss UTC\");\n }\n get moduleName(): string {\n return this.name;\n }\n\n private isLogLevel(level: LogLevel): boolean {\n return level <= this.logLevel;\n }\n\n public error(...props): Logger {\n if (!this.isLogLevel(LogLevel.Error)) return this;\n console.error(this.time, this.name, \"sdk logger error\", ...props);\n return this;\n }\n\n public logWithError(...props): Logger {\n // this.error(...props)\n const msg = props.map((arg) => (typeof arg === \"object\" ? JSON.stringify(arg) : arg)).join(\", \");\n throw new Error(msg);\n }\n\n public warning(...props): Logger {\n if (!this.isLogLevel(LogLevel.Warning)) return this;\n console.warn(this.time, this.name, \"sdk logger warning\", ...props);\n return this;\n }\n\n public info(...props): Logger {\n if (!this.isLogLevel(LogLevel.Info)) return this;\n console.info(this.time, this.name, \"sdk logger info\", ...props);\n return this;\n }\n\n public debug(...props): Logger {\n if (!this.isLogLevel(LogLevel.Debug)) return this;\n console.debug(this.time, this.name, \"sdk logger debug\", ...props);\n return this;\n }\n}\n\nconst moduleLoggers: { [key in ModuleName]?: Logger } = {};\nconst moduleLevels: { [key in ModuleName]?: LogLevel } = {};\n\nexport function createLogger(moduleName: string): Logger {\n let logger = get(moduleLoggers, moduleName);\n if (!logger) {\n // default level is error\n const logLevel = get(moduleLevels, moduleName);\n\n logger = new Logger({ name: moduleName, logLevel });\n set(moduleLoggers, moduleName, logger);\n }\n\n return logger;\n}\n\nexport function setLoggerLevel(moduleName: string, level: LogLevel): void {\n set(moduleLevels, moduleName, level);\n\n const logger = get(moduleLoggers, moduleName);\n if (logger) logger.level = level;\n}\n","import { PublicKey } from \"@solana/web3.js\";\nimport BN from \"bn.js\";\n\nimport { Fraction, Percent, Price, Token, TokenAmount } from \"../module\";\nimport { ReplaceType } from \"../raydium/type\";\n\nimport { tryParsePublicKey } from \"./pubKey\";\n\nexport async function sleep(ms: number): Promise<void> {\n new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function getTimestamp(): number {\n return new Date().getTime();\n}\n\nexport function notInnerObject(v: unknown): v is Record<string, any> {\n return (\n typeof v === \"object\" &&\n v !== null &&\n ![Token, TokenAmount, PublicKey, Fraction, BN, Price, Percent].some((o) => typeof o === \"object\" && v instanceof o)\n );\n}\n\nexport function jsonInfo2PoolKeys<T>(jsonInfo: T): ReplaceType<T, string, PublicKey> {\n // @ts-expect-error no need type for inner code\n return typeof jsonInfo === \"string\"\n ? tryParsePublicKey(jsonInfo)\n : Array.isArray(jsonInfo)\n ? jsonInfo.map((k) => jsonInfo2PoolKeys(k))\n : notInnerObject(jsonInfo)\n ? Object.fromEntries(Object.entries(jsonInfo).map(([k, v]) => [k, jsonInfo2PoolKeys(v)]))\n : jsonInfo;\n}\n","import _Big from \"big.js\";\nimport BN from \"bn.js\";\n\nimport { BigNumberish, BN_TEN, parseBigNumberish, Rounding } from \"../common/bignumber\";\nimport { createLogger, Logger } from \"../common/logger\";\n\nimport toFormat, { WrappedBig } from \"./formatter\";\nimport { Fraction } from \"./fraction\";\nimport { Token } from \"./token\";\nimport { Currency } from \"./currency\";\n\nconst logger = createLogger(\"Raydium_amount\");\n\nconst Big = toFormat(_Big);\ntype Big = WrappedBig;\n\nexport function splitNumber(num: string, decimals: number): [string, string] {\n let integral = \"0\";\n let fractional = \"0\";\n\n if (num.includes(\".\")) {\n const splited = num.split(\".\");\n if (splited.length === 2) {\n [integral, fractional] = splited;\n fractional = fractional.padEnd(decimals, \"0\");\n } else {\n logger.logWithError(`invalid number string, num: ${num}`);\n }\n } else {\n integral = num;\n }\n\n // fix decimals is 0\n return [integral, fractional.slice(0, decimals) || fractional];\n}\n\nexport class TokenAmount extends Fraction {\n public readonly token: Token;\n protected logger: Logger;\n\n public constructor(token: Token, amount: BigNumberish, isRaw = true, name?: string) {\n let parsedAmount = new BN(0);\n const multiplier = BN_TEN.pow(new BN(token.decimals));\n\n if (isRaw) {\n parsedAmount = parseBigNumberish(amount);\n } else {\n let integralAmount = new BN(0);\n let fractionalAmount = new BN(0);\n\n // parse fractional string\n if (typeof amount === \"string\" || typeof amount === \"number\" || typeof amount === \"bigint\") {\n const [integral, fractional] = splitNumber(amount.toString(), token.decimals);\n integralAmount = parseBigNumberish(integral);\n fractionalAmount = parseBigNumberish(fractional);\n }\n\n integralAmount = integralAmount.mul(multiplier);\n parsedAmount = integralAmount.add(fractionalAmount);\n }\n\n super(parsedAmount, multiplier);\n this.logger = createLogger(name || \"TokenAmount\");\n this.token = token;\n }\n\n public get raw(): BN {\n return this.numerator;\n }\n public isZero(): boolean {\n return this.raw.isZero();\n }\n public gt(other: TokenAmount): boolean {\n if (!this.token.equals(other.token)) this.logger.logWithError(\"gt token not equals\");\n return this.raw.gt(other.raw);\n }\n\n /**\n * a less than b\n */\n public lt(other: TokenAmount): boolean {\n if (!this.token.equals(other.token)) this.logger.logWithError(\"lt token not equals\");\n return this.raw.lt(other.raw);\n }\n\n public add(other: TokenAmount): TokenAmount {\n if (!this.token.equals(other.token)) this.logger.logWithError(\"add token not equals\");\n return new TokenAmount(this.token, this.raw.add(other.raw));\n }\n\n public subtract(other: TokenAmount): TokenAmount {\n if (!this.token.equals(other.token)) this.logger.logWithError(\"sub token not equals\");\n return new TokenAmount(this.token, this.raw.sub(other.raw));\n }\n\n public toSignificant(\n significantDigits = this.token.decimals,\n format?: object,\n rounding: Rounding = Rounding.ROUND_DOWN,\n ): string {\n return super.toSignificant(significantDigits, format, rounding);\n }\n\n /**\n * To fixed\n *\n * @example\n * ```\n * 1 -> 1.000000000\n * 1.234 -> 1.234000000\n * 1.123456789876543 -> 1.123456789\n * ```\n */\n public toFixed(\n decimalPlaces = this.token.decimals,\n format?: object,\n rounding: Rounding = Rounding.ROUND_DOWN,\n ): string {\n if (decimalPlaces > this.token.decimals) this.logger.logWithError(\"decimals overflow\");\n return super.toFixed(decimalPlaces, format, rounding);\n }\n\n /**\n * To exact\n *\n * @example\n * ```\n * 1 -> 1\n * 1.234 -> 1.234\n * 1.123456789876543 -> 1.123456789\n * ```\n */\n public toExact(format: object = { groupSeparator: \"\" }): string {\n Big.DP = this.token.decimals;\n return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(format);\n }\n}\n\nexport class CurrencyAmount extends Fraction {\n public readonly currency: Currency;\n protected logger: Logger;\n\n public constructor(currency: Currency, amount: BigNumberish, isRaw = true, name?: string) {\n let parsedAmount = new BN(0);\n const multiplier = BN_TEN.pow(new BN(currency.decimals));\n\n if (isRaw) {\n parsedAmount = parseBigNumberish(amount);\n } else {\n let integralAmount = new BN(0);\n let fractionalAmount = new BN(0);\n\n // parse fractional string\n if (typeof amount === \"string\" || typeof amount === \"number\" || typeof amount === \"bigint\") {\n const [integral, fractional] = splitNumber(amount.toString(), currency.decimals);\n integralAmount = parseBigNumberish(integral);\n fractionalAmount = parseBigNumberish(fractional);\n }\n\n integralAmount = integralAmount.mul(multiplier);\n parsedAmount = integralAmount.add(fractionalAmount);\n }\n\n super(parsedAmount, multiplier);\n this.logger = createLogger(name || \"TokenAmount\");\n this.currency = currency;\n }\n\n public get raw(): BN {\n return this.numerator;\n }\n\n public isZero(): boolean {\n return this.raw.isZero();\n }\n\n /**\n * a greater than b\n */\n public gt(other: CurrencyAmount): boolean {\n if (!this.currency.equals(other.currency)) this.logger.logWithError(\"gt currency not equals\");\n return this.raw.gt(other.raw);\n }\n\n /**\n * a less than b\n */\n public lt(other: CurrencyAmount): boolean {\n if (!this.currency.equals(other.currency)) this.logger.logWithError(\"lt currency not equals\");\n return this.raw.lt(other.raw);\n }\n\n public add(other: CurrencyAmount): CurrencyAmount {\n if (!this.currency.equals(other.currency)) this.logger.logWithError(\"add currency not equals\");\n return new CurrencyAmount(this.currency, this.raw.add(other.raw));\n }\n\n public sub(other: CurrencyAmount): CurrencyAmount {\n if (!this.currency.equals(other.currency)) this.logger.logWithError(\"sub currency not equals\");\n return new CurrencyAmount(this.currency, this.raw.sub(other.raw));\n }\n\n public toSignificant(\n significantDigits = this.currency.decimals,\n format?: object,\n rounding: Rounding = Rounding.ROUND_DOWN,\n ): string {\n return super.toSignificant(significantDigits, format, rounding);\n }\n\n /**\n * To fixed\n *\n * @example\n * ```\n * 1 -> 1.000000000\n * 1.234 -> 1.234000000\n * 1.123456789876543 -> 1.123456789\n * ```\n */\n public toFixed(\n decimalPlaces = this.currency.decimals,\n format?: object,\n rounding: Rounding = Rounding.ROUND_DOWN,\n ): string {\n if (decimalPlaces > this.currency.decimals) this.logger.logWithError(\"decimals overflow\");\n\n return super.toFixed(decimalPlaces, format, rounding);\n }\n\n /**\n * To exact\n *\n * @example\n * ```\n * 1 -> 1\n * 1.234 -> 1.234\n * 1.123456789876543 -> 1.123456789\n * ```\n */\n public toExact(format: object = { groupSeparator: \"\" }): string {\n Big.DP = this.currency.decimals;\n return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(format);\n }\n}\n","import BN from \"bn.js\";\nimport Decimal from \"decimal.js\";\nimport { Token } from \"../module/token\";\nimport { Price } from \"../module/price\";\nimport { Currency } from \"../module/currency\";\nimport { TokenAmount, CurrencyAmount } from \"../module/amount\";\nimport { Fraction } from \"../module/fraction\";\nimport { Percent } from \"../module/percent\";\nimport { SplToken, TokenJson } from \"../raydium/token/type\";\nimport { ReplaceType } from \"../raydium/type\";\nimport { createLogger } from \"./logger\";\nimport { mul } from \"./fractionUtil\";\nimport { notInnerObject } from \"./utility\";\n\nexport enum Rounding {\n ROUND_DOWN,\n ROUND_HALF_UP,\n ROUND_UP,\n}\n\nexport const BN_ZERO = new BN(0);\nexport const BN_ONE = new BN(1);\nexport const BN_TWO = new BN(2);\nexport const BN_THREE = new BN(3);\nexport const BN_FIVE = new BN(5);\nexport const BN_TEN = new BN(10);\nexport const BN_100 = new BN(100);\nexport const BN_1000 = new BN(1000);\nexport const BN_10000 = new BN(10000);\nexport type BigNumberish = BN | string | number | bigint;\nexport type Numberish = number | string | bigint | Fraction | BN;\n\nconst MAX_SAFE = 0x1fffffffffffff;\n\nexport function parseBigNumberish(value: BigNumberish): BN {\n const logger = createLogger(\"Raydium_parseBigNumberish\");\n // BN\n if (value instanceof BN) {\n return value;\n }\n\n if (typeof value === \"string\") {\n if (value.match(/^-?[0-9]+$/)) {\n return new BN(value);\n }\n logger.logWithError(`invalid BigNumberish string: ${value}`);\n }\n\n if (typeof value === \"number\") {\n if (value % 1) {\n logger.logWithError(`BigNumberish number underflow: ${value}`);\n }\n\n if (value >= MAX_SAFE || value <= -MAX_SAFE) {\n logger.logWithError(`BigNumberish number overflow: ${value}`);\n }\n\n return new BN(String(value));\n }\n\n if (typeof value === \"bigint\") {\n return new BN(value.toString());\n }\n logger.error(`invalid BigNumberish value: ${value}`);\n return new BN(0); // never reach, because logWithError will throw error\n}\n\nexport function tenExponential(shift: BigNumberish): BN {\n return BN_TEN.pow(parseBigNumberish(shift));\n}\n\n/**\n *\n * @example\n * getIntInfo(0.34) => { numerator: '34', denominator: '100'}\n * getIntInfo('0.34') //=> { numerator: '34', denominator: '100'}\n */\nexport function parseNumberInfo(n: Numberish | undefined): {\n denominator: string;\n numerator: string;\n sign?: string;\n int?: string;\n dec?: string;\n} {\n if (n === undefined) return { denominator: \"1\", numerator: \"0\" };\n if (n instanceof BN) {\n return { numerator: n.toString(), denominator: \"1\" };\n }\n\n if (n instanceof Fraction) {\n return { denominator: n.denominator.toString(), numerator: n.numerator.toString() };\n }\n\n const s = String(n);\n const [, sign = \"\", int = \"\", dec = \"\"] = s.replace(\",\", \"\").match(/(-?)(\\d*)\\.?(\\d*)/) ?? [];\n const denominator = \"1\" + \"0\".repeat(dec.length);\n const numerator = sign + (int === \"0\" ? \"\" : int) + dec || \"0\";\n return { denominator, numerator, sign, int, dec };\n}\n\n// round up\nexport function divCeil(a: BN, b: BN): BN {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const dm = a.divmod(b);\n\n // Fast case - exact division\n if (dm.mod.isZero()) return dm.div;\n\n // Round up\n return dm.div.isNeg() ? dm.div.isubn(1) : dm.div.iaddn(1);\n}\n\nexport function shakeFractionDecimal(n: Fraction): string {\n const [, sign = \"\", int = \"\"] = n.toFixed(2).match(/(-?)(\\d*)\\.?(\\d*)/) ?? [];\n return `${sign}${int}`;\n}\n\nexport function toBN(n: Numberish, decimal: BigNumberish = 0): BN {\n if (n instanceof BN) return n;\n return new BN(shakeFractionDecimal(toFraction(n).mul(BN_TEN.pow(new BN(String(decimal))))));\n}\n\nexport function toFraction(value: Numberish): Fraction {\n // to complete math format(may have decimal), not int\n if (value instanceof Percent) return new Fraction(value.numerator, value.denominator);\n\n if (value instanceof Price) return value.adjusted;\n\n // to complete math format(may have decimal), not BN\n if (value instanceof TokenAmount)\n try {\n return toFraction(value.toExact());\n } catch {\n return new Fraction(BN_ZERO);\n }\n\n // do not ideal with other fraction value\n if (value instanceof Fraction) return value;\n\n // wrap to Fraction\n const n = String(value);\n const details = parseNumberInfo(n);\n return new Fraction(details.numerator, details.denominator);\n}\n\n/**\n * @example\n * toPercent(3.14) // => Percent { 314.00% }\n * toPercent(3.14, { alreadyDecimaled: true }) // => Percent {3.14%}\n */\nexport function toPercent(\n n: Numberish,\n options?: { /* usually used for backend data */ alreadyDecimaled?: boolean },\n): Percent {\n const { numerator, denominator } = parseNumberInfo(n);\n return new Percent(new BN(numerator), new BN(denominator).mul(options?.alreadyDecimaled ? new BN(100) : new BN(1)));\n}\n\nexport function toTokenPrice(params: {\n token: TokenJson | Token | SplToken;\n numberPrice: Numberish;\n decimalDone?: boolean;\n}): Price {\n const { token, numberPrice, decimalDone } = params;\n const usdCurrency = new Token({ mint: \"\", decimals: 6, symbol: \"usd\", name: \"usd\", skipMint: true });\n const { numerator, denominator } = parseNumberInfo(numberPrice);\n const parsedNumerator = decimalDone ? new BN(numerator).mul(BN_TEN.pow(new BN(token.decimals))) : numerator;\n const parsedDenominator = new BN(denominator).mul(BN_TEN.pow(new BN(usdCurrency.decimals)));\n\n return new Price({\n baseToken: usdCurrency,\n denominator: parsedDenominator.toString(),\n quoteToken: new Token({ ...token, skipMint: true, mint: \"\" }),\n numerator: parsedNumerator.toString(),\n });\n}\n\nexport function toUsdCurrency(amount: Numberish): CurrencyAmount {\n const usdCurrency = new Currency({ decimals: 6, symbol: \"usd\", name: \"usd\" });\n const amountBigNumber = toBN(mul(amount, 10 ** usdCurrency.decimals)!);\n return new CurrencyAmount(usdCurrency, amountBigNumber);\n}\n\nexport function toTotalPrice(amount: Numberish | undefined, price: Price | undefined): CurrencyAmount {\n if (!price || !amount) return toUsdCurrency(0);\n return toUsdCurrency(mul(amount, price)!);\n}\n\nexport function decimalToFraction(n: Decimal | undefined): Fraction | undefined {\n if (n == null) return undefined;\n const { numerator, denominator } = parseNumberInfo(n.toString());\n return new Fraction(numerator, denominator);\n}\n\nexport function isDecimal(val: unknown): boolean {\n return val instanceof Decimal;\n}\n\nexport function recursivelyDecimalToFraction<T>(info: T): ReplaceType<T, Decimal, Fraction> {\n // @ts-expect-error no need type for inner code\n return isDecimal(info)\n ? d