@raydium-io/raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
1,434 lines (1,324 loc) • 72.7 kB
text/typescript
import { EpochInfo, PublicKey, SystemProgram } from "@solana/web3.js";
import {
createTransferInstruction,
TOKEN_PROGRAM_ID,
TOKEN_2022_PROGRAM_ID,
createAssociatedTokenAccountIdempotentInstruction,
TransferFee,
TransferFeeConfig,
createSyncNativeInstruction,
} from "@solana/spl-token";
import BN from "bn.js";
import Decimal from "decimal.js";
import { AmmV4Keys, ApiV3PoolInfoConcentratedItem, ApiV3Token, ClmmKeys, PoolKeys } from "@/api";
import {
AMM_V4,
BigNumberish,
CLMM_PROGRAM_ID,
CREATE_CPMM_POOL_PROGRAM,
fetchMultipleMintInfos,
getMultipleAccountsInfoWithCustomFlags,
minExpirationTime,
parseBigNumberish,
solToWSol,
WSOLMint,
} from "@/common";
import { MakeMultiTxData, MakeTxData } from "@/common/txTool/txTool";
import { InstructionType, TxVersion } from "@/common/txTool/txType";
import { publicKey, struct } from "../../marshmallow";
import { Price, TokenAmount } from "../../module";
import {
ClmmInstrument,
ClmmParsedRpcData,
ComputeClmmPoolInfo,
MAX_SQRT_PRICE_X64,
MIN_SQRT_PRICE_X64,
PoolUtils,
ReturnTypeComputeAmountOutBaseOut,
ReturnTypeComputeAmountOutFormat,
ReturnTypeFetchMultiplePoolTickArrays,
SqrtPriceMath,
} from "../../raydium/clmm";
import { PoolInfoLayout } from "../../raydium/clmm/layout";
import { CpmmPoolInfoLayout, getPdaPoolAuthority } from "../../raydium/cpmm";
import {
ComputeAmountOutParam,
getLiquidityAssociatedAuthority,
liquidityStateV4Layout,
toAmmComputePoolInfo,
} from "../../raydium/liquidity";
import { ComputeBudgetConfig, ReturnTypeFetchMultipleMintInfos, TransferAmountFee } from "../../raydium/type";
import { closeAccountInstruction, createWSolAccountInstructions } from "../account/instruction";
import { TokenAccount } from "../account/types";
import { CpmmComputeData } from "../cpmm";
import { AmmRpcData } from "../liquidity";
import ModuleBase, { ModuleBaseProps } from "../moduleBase";
import { Market, MARKET_STATE_LAYOUT_V3 } from "../serum";
import { toApiV3Token, toToken, toTokenAmount } from "../token";
import { makeSwapInstruction } from "./instrument";
import {
BasicPoolInfo,
ComputeAmountOutAmmLayout,
ComputeAmountOutLayout,
ComputePoolType,
ComputeRoutePathType,
ReturnTypeFetchMultipleInfo,
ReturnTypeGetAllRoute,
RoutePathType,
} from "./type";
import {
buyExactInInstruction,
Curve,
getPdaCreatorVault,
getPdaLaunchpadAuth,
getPdaPlatformVault,
LaunchpadConfigInfo,
LaunchpadPlatformInfo,
LaunchpadPoolInfo,
PlatformConfig,
sellExactInInstruction,
SwapInfoReturn,
} from "../launchpad";
const ZERO = new BN(0);
export default class TradeV2 extends ModuleBase {
constructor(params: ModuleBaseProps) {
super(params);
}
private async getWSolAccounts(): Promise<TokenAccount[]> {
this.scope.checkOwner();
await this.scope.account.fetchWalletTokenAccounts();
const tokenAccounts = this.scope.account.tokenAccounts.filter((acc) => acc.mint.equals(WSOLMint));
tokenAccounts.sort((a, b) => {
if (a.isAssociated) return 1;
if (b.isAssociated) return -1;
return a.amount.lt(b.amount) ? -1 : 1;
});
return tokenAccounts;
}
public async unWrapWSol<T extends TxVersion>(props: {
amount: BigNumberish;
computeBudgetConfig?: ComputeBudgetConfig;
tokenProgram?: PublicKey;
txVersion?: T;
feePayer?: PublicKey;
}): Promise<MakeTxData<T>> {
const { amount, tokenProgram, txVersion = TxVersion.LEGACY, feePayer } = props;
const tokenAccounts = await this.getWSolAccounts();
const txBuilder = this.createTxBuilder(feePayer);
txBuilder.addCustomComputeBudget(props.computeBudgetConfig);
// const ins = await createWSolAccountInstructions({
// connection: this.scope.connection,
// owner: this.scope.ownerPubKey,
// payer: this.scope.ownerPubKey,
// amount: 0,
// });
// txBuilder.addInstruction(ins);
const amountBN = parseBigNumberish(amount);
for (let i = 0; i < tokenAccounts.length; i++) {
if (amountBN.gte(tokenAccounts[i].amount)) {
txBuilder.addInstruction({
instructions: [
closeAccountInstruction({
tokenAccount: tokenAccounts[i].publicKey!,
payer: this.scope.ownerPubKey,
owner: this.scope.ownerPubKey,
programId: tokenProgram,
}),
],
});
amountBN.sub(tokenAccounts[i].amount);
} else {
txBuilder.addInstruction({
instructions: [
closeAccountInstruction({
tokenAccount: tokenAccounts[i].publicKey!,
payer: this.scope.ownerPubKey,
owner: this.scope.ownerPubKey,
programId: tokenProgram,
}),
],
});
}
}
return txBuilder.versionBuild({ txVersion }) as Promise<MakeTxData<T>>;
}
public async wrapWSol<T extends TxVersion>(
amount: BigNumberish,
tokenProgram?: PublicKey,
txVersion?: T,
feePayer?: PublicKey,
): Promise<MakeTxData<T>> {
// const tokenAccounts = await this.getWSolAccounts();
const txBuilder = this.createTxBuilder(feePayer);
const ins = await createWSolAccountInstructions({
connection: this.scope.connection,
owner: this.scope.ownerPubKey,
payer: this.scope.ownerPubKey,
amount,
skipCloseAccount: true,
});
txBuilder.addInstruction(ins);
// if (tokenAccounts.length) {
// // already have wsol account
// txBuilder.addInstruction({
// instructions: [
// makeTransferInstruction({
// destination: tokenAccounts[0].publicKey!,
// source: ins.addresses.newAccount,
// amount,
// owner: this.scope.ownerPubKey,
// tokenProgram,
// }),
// ],
// endInstructions: [
// closeAccountInstruction({
// tokenAccount: ins.addresses.newAccount,
// payer: this.scope.ownerPubKey,
// owner: this.scope.ownerPubKey,
// programId: tokenProgram,
// }),
// ],
// });
// }
return txBuilder.versionBuild({ txVersion: txVersion ?? TxVersion.LEGACY }) as Promise<MakeTxData<T>>;
}
public async swap<T extends TxVersion>({
swapInfo,
swapPoolKeys,
ownerInfo,
computeBudgetConfig,
routeProgram,
txVersion,
feePayer,
}: {
txVersion: T;
swapInfo: ComputeAmountOutLayout;
swapPoolKeys?: PoolKeys[];
ownerInfo: {
associatedOnly: boolean;
checkCreateATAOwner: boolean;
};
routeProgram: PublicKey;
computeBudgetConfig?: ComputeBudgetConfig;
feePayer?: PublicKey;
}): Promise<MakeMultiTxData<T>> {
const txBuilder = this.createTxBuilder(feePayer);
const amountIn = swapInfo.amountIn;
const amountOut = swapInfo.amountOut;
const useSolBalance = amountIn.amount.token.mint.equals(WSOLMint);
const isOutputSol = amountOut.amount.token.mint.equals(WSOLMint);
const inputMint = amountIn.amount.token.mint;
const outputMint = amountOut.amount.token.mint;
const { account: sourceAcc, instructionParams: sourceAccInsParams } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: amountIn.amount.token.isToken2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
mint: inputMint,
notUseTokenAccount: useSolBalance,
owner: this.scope.ownerPubKey,
skipCloseAccount: !useSolBalance,
createInfo: useSolBalance
? {
payer: this.scope.ownerPubKey,
amount: amountIn.amount.raw,
}
: undefined,
associatedOnly: useSolBalance ? false : ownerInfo.associatedOnly,
checkCreateATAOwner: ownerInfo.checkCreateATAOwner,
});
sourceAccInsParams && txBuilder.addInstruction(sourceAccInsParams);
if (sourceAcc === undefined) {
throw Error("input account check error");
}
let destinationAcc: PublicKey;
if (swapInfo.routeType === "route" && !isOutputSol) {
destinationAcc = this.scope.account.getAssociatedTokenAccount(
outputMint,
amountOut.amount.token.isToken2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
);
} else {
const { account, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: amountOut.amount.token.isToken2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
mint: outputMint,
notUseTokenAccount: isOutputSol,
owner: this.scope.ownerPubKey,
skipCloseAccount: true,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
associatedOnly: isOutputSol ? false : ownerInfo.associatedOnly,
checkCreateATAOwner: ownerInfo.checkCreateATAOwner,
});
destinationAcc = account!;
instructionParams && txBuilder.addInstruction(instructionParams);
}
if (isOutputSol) {
txBuilder.addInstruction({
endInstructions: [
closeAccountInstruction({
owner: this.scope.ownerPubKey,
payer: this.scope.ownerPubKey,
tokenAccount: destinationAcc,
programId: TOKEN_PROGRAM_ID,
}),
],
endInstructionTypes: [InstructionType.CloseAccount],
});
}
let routeTokenAcc: PublicKey | undefined = undefined;
if (swapInfo.routeType === "route") {
const middleMint = swapInfo.middleToken;
routeTokenAcc = this.scope.account.getAssociatedTokenAccount(
middleMint.mint,
middleMint.isToken2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
);
}
const poolKeys = swapPoolKeys ? swapPoolKeys : await this.computePoolToPoolKeys({ pools: swapInfo.poolInfoList });
const swapIns = makeSwapInstruction({
routeProgram,
inputMint,
swapInfo: {
...swapInfo,
poolInfo: [...swapInfo.poolInfoList],
poolKey: poolKeys,
outputMint,
},
ownerInfo: {
wallet: this.scope.ownerPubKey,
sourceToken: sourceAcc,
routeToken: routeTokenAcc,
destinationToken: destinationAcc!,
},
});
if (swapInfo.feeConfig !== undefined) {
const checkTxBuilder = this.createTxBuilder();
checkTxBuilder.addInstruction({
instructions: [
createTransferInstruction(
sourceAcc,
swapInfo.feeConfig.feeAccount,
this.scope.ownerPubKey,
swapInfo.feeConfig.feeAmount.toNumber(),
),
],
instructionTypes: [InstructionType.TransferAmount],
});
checkTxBuilder.addInstruction(swapIns);
const { transactions } =
txVersion === TxVersion.V0 ? await checkTxBuilder.sizeCheckBuildV0() : await checkTxBuilder.sizeCheckBuild();
if (transactions.length < 2) {
txBuilder.addInstruction({
instructions: [
createTransferInstruction(
sourceAcc,
swapInfo.feeConfig.feeAccount,
this.scope.ownerPubKey,
swapInfo.feeConfig.feeAmount.toNumber(),
),
],
instructionTypes: [InstructionType.TransferAmount],
});
}
}
txBuilder.addInstruction(swapIns);
if (txVersion === TxVersion.V0)
return txBuilder.sizeCheckBuildV0({ computeBudgetConfig, address: swapIns.address }) as Promise<
MakeMultiTxData<T>
>;
return txBuilder.sizeCheckBuild({ computeBudgetConfig, address: swapIns.address }) as Promise<MakeMultiTxData<T>>;
}
public async swapClmmToLaunchMint<T extends TxVersion>({
inputMint,
inputAmount,
fixClmmOut = false,
clmmPoolId,
launchPoolId,
priceLimit,
slippage = 0.01,
shareFeeRate = new BN(0),
shareFeeReceiver,
launchPlatformInfo,
slot,
mintInfo,
epochInfo: propsEpochInfo,
ownerInfo = { useSOLBalance: true },
checkCreateATAOwner = false,
computeBudgetConfig,
txVersion,
}: {
inputMint: string | PublicKey;
inputAmount: BN;
fixClmmOut?: boolean;
clmmPoolId: string | PublicKey;
launchPoolId: string | PublicKey;
priceLimit?: Decimal;
epochInfo?: EpochInfo;
slippage: number; // from 0~1
shareFeeRate?: BN;
shareFeeReceiver?: PublicKey;
launchPlatformInfo?: Pick<LaunchpadPlatformInfo, "feeRate" | "creatorFeeRate">;
slot?: number;
mintInfo?: ApiV3Token;
ownerInfo?: {
useSOLBalance?: boolean;
feePayer?: PublicKey;
};
checkCreateATAOwner?: boolean;
computeBudgetConfig?: ComputeBudgetConfig;
txVersion: T;
}): Promise<
MakeTxData<
T,
{
routes: { mint: PublicKey; amount: BN; decimal: number }[];
outAmount: BN;
minOutAmount: BN;
}
>
> {
const feePayer = ownerInfo?.feePayer || this.scope.ownerPubKey;
const epochInfo = propsEpochInfo ?? (await this.scope.fetchEpochInfo());
const {
clmmPoolData,
clmmComputeAmount: { maxClmmAmountIn, clmmAmountOut, remainingAccounts },
launchPoolInfo,
launchAuthProgramId,
launchSwapInfo,
outAmount,
minOutAmount,
} = await this.computeClmmToLaunchAmount({
inputMint,
inputAmount,
fixClmmOut,
clmmPoolId,
launchPoolId,
slippage,
epochInfo,
shareFeeRate,
launchPlatformInfo,
slot,
mintInfo,
});
const baseIn = inputMint.toString() === clmmPoolData.poolInfo.mintA.address;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && clmmPoolData.poolInfo.mintA.address === WSOLMint.toBase58();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && clmmPoolData.poolInfo.mintB.address === WSOLMint.toBase58();
const tokenAccountMap: Record<string, PublicKey> = {};
let sqrtPriceLimitX64: BN;
if (!priceLimit || priceLimit.equals(new Decimal(0))) {
sqrtPriceLimitX64 = baseIn ? MIN_SQRT_PRICE_X64.add(new BN(1)) : MAX_SQRT_PRICE_X64.sub(new BN(1));
} else {
sqrtPriceLimitX64 = SqrtPriceMath.priceToSqrtPriceX64(
priceLimit,
clmmPoolData.poolInfo.mintA.decimals,
clmmPoolData.poolInfo.mintB.decimals,
);
}
const txBuilder = this.createTxBuilder(feePayer);
const [clmmMintA, clmmMintB] = [
new PublicKey(clmmPoolData.poolInfo.mintA.address),
new PublicKey(clmmPoolData.poolInfo.mintB.address),
];
const [clmmMintAProgram, clmmMintBProgram] = [
new PublicKey(clmmPoolData.poolInfo.mintA.programId),
new PublicKey(clmmPoolData.poolInfo.mintB.programId),
];
const ownerTokenAccountA = this.scope.account.getAssociatedTokenAccount(clmmMintA, clmmMintAProgram);
const ownerTokenAccountB = this.scope.account.getAssociatedTokenAccount(clmmMintB, clmmMintBProgram);
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
ownerTokenAccountA,
this.scope.ownerPubKey,
clmmMintA,
clmmMintAProgram,
),
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
ownerTokenAccountB,
this.scope.ownerPubKey,
clmmMintB,
clmmMintBProgram,
),
],
});
if ((baseIn && mintAUseSOLBalance) || (!baseIn && mintBUseSOLBalance)) {
txBuilder.addInstruction({
instructions: [
SystemProgram.transfer({
fromPubkey: this.scope.ownerPubKey,
toPubkey: baseIn ? ownerTokenAccountA : ownerTokenAccountB,
lamports: BigInt(maxClmmAmountIn.toString()),
}),
createSyncNativeInstruction(baseIn ? ownerTokenAccountA : ownerTokenAccountB),
],
});
}
tokenAccountMap[clmmPoolData.poolInfo.mintA.address] = ownerTokenAccountA;
tokenAccountMap[clmmPoolData.poolInfo.mintB.address] = ownerTokenAccountB;
if (!ownerTokenAccountA || !ownerTokenAccountB)
this.logAndCreateError("user do not have token account", {
ownerTokenAccountA,
ownerTokenAccountB,
});
txBuilder.addInstruction(
fixClmmOut
? ClmmInstrument.makeSwapBaseOutInstructions({
poolInfo: clmmPoolData.poolInfo,
poolKeys: clmmPoolData.poolKeys,
observationId: clmmPoolData.computePoolInfo.observationId,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
outputMint: baseIn ? clmmMintB : clmmMintA,
amountOut: clmmAmountOut,
amountInMax: maxClmmAmountIn,
sqrtPriceLimitX64,
remainingAccounts,
})
: ClmmInstrument.makeSwapBaseInInstructions({
poolInfo: clmmPoolData.poolInfo,
poolKeys: clmmPoolData.poolKeys,
observationId: clmmPoolData.computePoolInfo.observationId,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
inputMint: new PublicKey(inputMint),
amountIn: inputAmount,
amountOutMin: clmmAmountOut,
sqrtPriceLimitX64,
remainingAccounts,
}),
);
const launchMintAProgram = launchPoolInfo.mintProgramFlag === 0 ? TOKEN_PROGRAM_ID : TOKEN_2022_PROGRAM_ID;
const launchTokenAccountA = this.scope.account.getAssociatedTokenAccount(launchPoolInfo.mintA, launchMintAProgram);
let launchTokenAccountB = tokenAccountMap[launchPoolInfo.mintB.toBase58()];
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
launchTokenAccountA,
this.scope.ownerPubKey,
launchPoolInfo.mintA,
launchMintAProgram,
),
],
});
if (!launchTokenAccountB) {
const mintBUseSol = launchPoolInfo.mintB.equals(WSOLMint);
const { account, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: TOKEN_PROGRAM_ID,
mint: launchPoolInfo.mintB,
notUseTokenAccount: mintBUseSol,
owner: this.scope.ownerPubKey,
skipCloseAccount: !mintBUseSol,
createInfo: mintBUseSol
? {
payer: this.scope.ownerPubKey!,
amount: clmmAmountOut,
}
: undefined,
associatedOnly: false,
checkCreateATAOwner,
});
launchTokenAccountB = account!;
instructionParams && txBuilder.addInstruction(instructionParams);
}
txBuilder.addInstruction({
instructions: [
buyExactInInstruction(
launchPoolInfo.programId,
this.scope.ownerPubKey,
launchAuthProgramId,
launchPoolInfo.configId,
launchPoolInfo.platformId,
new PublicKey(launchPoolId),
launchTokenAccountA,
launchTokenAccountB,
launchPoolInfo.vaultA,
launchPoolInfo.vaultB,
launchPoolInfo.mintA,
launchPoolInfo.mintB,
launchMintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(launchPoolInfo.programId, launchPoolInfo.platformId, launchPoolInfo.mintB).publicKey,
getPdaCreatorVault(launchPoolInfo.programId, launchPoolInfo.creator, launchPoolInfo.mintB).publicKey,
launchSwapInfo.amountB.lt(clmmAmountOut) ? launchSwapInfo.amountB : clmmAmountOut,
minOutAmount,
shareFeeRate,
shareFeeReceiver,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
return txBuilder.versionBuild({
txVersion,
extInfo: {
routes: [
{
mint: new PublicKey(inputMint),
amount: fixClmmOut ? maxClmmAmountIn : inputAmount,
decimal: clmmPoolData.poolInfo[baseIn ? "mintA" : "mintB"].decimals,
},
{
mint: baseIn ? clmmMintB : clmmMintA,
amount: clmmAmountOut,
decimal: clmmPoolData.poolInfo[baseIn ? "mintB" : "mintA"].decimals,
},
{
mint: launchPoolInfo.mintA,
amount: outAmount,
decimal: launchPoolInfo.mintDecimalsA,
},
],
outAmount,
minOutAmount,
},
}) as Promise<
MakeTxData<
T,
{
routes: { mint: PublicKey; amount: BN; decimal: number }[];
outAmount: BN;
minOutAmount: BN;
}
>
>;
}
public async computeClmmToLaunchAmount({
inputMint,
inputAmount,
fixClmmOut = false,
clmmPoolId,
launchPoolId,
slippage: propsSlippage,
epochInfo,
shareFeeRate = new BN(0),
clmmPoolData: propsClmmPoolData,
launchPoolInfo: propsLaunchPoolInfo,
launchPlatformInfo: propsLaunchPlatformInfo,
slot,
mintInfo: propsMintInfo,
}: {
clmmPoolId: string | PublicKey;
launchPoolId: string | PublicKey;
inputMint: string | PublicKey;
inputAmount: BN;
fixClmmOut?: boolean;
slippage: number;
epochInfo?: EpochInfo;
shareFeeRate?: BN;
clmmPoolData?: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
computePoolInfo: ComputeClmmPoolInfo;
tickData: ReturnTypeFetchMultiplePoolTickArrays;
};
launchPoolInfo?: LaunchpadPoolInfo & { programId: PublicKey; configInfo: LaunchpadConfigInfo };
launchPlatformInfo?: Pick<LaunchpadPlatformInfo, "feeRate" | "creatorFeeRate">;
slot?: number;
mintInfo?: ApiV3Token;
}): Promise<{
clmmPoolData: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
computePoolInfo: ComputeClmmPoolInfo;
tickData: ReturnTypeFetchMultiplePoolTickArrays;
};
clmmComputeAmount: { maxClmmAmountIn: BN; clmmAmountOut: BN; remainingAccounts: PublicKey[] };
clmmComputeInfo: ReturnTypeComputeAmountOutBaseOut | ReturnTypeComputeAmountOutFormat;
launchPoolInfo: LaunchpadPoolInfo & { programId: PublicKey; configInfo: LaunchpadConfigInfo };
launchAuthProgramId: PublicKey;
outAmount: BN;
minOutAmount: BN;
launchSwapInfo: SwapInfoReturn;
launchMintTransferFeeConfig?: TransferFeeConfig;
}> {
// split slippage for clmm swap and launch buy
const slippage =
propsSlippage > 0
? new Decimal(propsSlippage).div(2).toDecimalPlaces(4, Decimal.ROUND_DOWN).toNumber()
: propsSlippage;
const clmmPoolData = propsClmmPoolData ?? (await this.scope.clmm.getPoolInfoFromRpc(clmmPoolId.toString()));
if (
inputMint.toString() !== clmmPoolData.poolInfo.mintA.address &&
inputMint.toString() !== clmmPoolData.poolInfo.mintB.address
)
throw new Error("input mint does not match clmm pool mints, please check");
const baseIn = inputMint.toString() === clmmPoolData.poolInfo.mintA.address;
const tokenOut = clmmPoolData.poolInfo[baseIn ? "mintB" : "mintA"];
const clmmComputeAmount = fixClmmOut
? await PoolUtils.computeAmountIn({
poolInfo: clmmPoolData.computePoolInfo,
tickArrayCache: clmmPoolData.tickData[clmmPoolId.toString()],
amountOut: inputAmount,
baseMint: new PublicKey(clmmPoolData.poolInfo[baseIn ? "mintB" : "mintA"].address),
slippage,
epochInfo: epochInfo ?? (await this.scope.fetchEpochInfo()),
})
: await PoolUtils.computeAmountOutFormat({
poolInfo: clmmPoolData.computePoolInfo,
tickArrayCache: clmmPoolData.tickData[clmmPoolId.toString()],
amountIn: inputAmount,
tokenOut,
slippage,
epochInfo: epochInfo ?? (await this.scope.fetchEpochInfo()),
});
let launchPoolInfo = propsLaunchPoolInfo;
if (!launchPoolInfo)
launchPoolInfo = await this.scope.launchpad.getRpcPoolInfo({ poolId: new PublicKey(launchPoolId) });
if (tokenOut.address !== launchPoolInfo.mintB.toBase58())
throw new Error(`clmm swap mint(${tokenOut.address}) != launch pool mintB(${launchPoolInfo.mintB.toBase58()})`);
let platformInfo = propsLaunchPlatformInfo;
if (!platformInfo) {
const data = await this.scope.connection.getAccountInfo(launchPoolInfo.platformId);
platformInfo = PlatformConfig.decode(data!.data);
}
const mintInfo = propsMintInfo ?? (await this.scope.token.getTokenInfo(launchPoolInfo.mintA));
const authProgramId = getPdaLaunchpadAuth(launchPoolInfo.programId).publicKey;
const launchMintTransferFeeConfig = mintInfo.extensions.feeConfig
? {
transferFeeConfigAuthority: PublicKey.default,
withdrawWithheldAuthority: PublicKey.default,
withheldAmount: BigInt(0),
olderTransferFee: {
epoch: BigInt(mintInfo.extensions.feeConfig.olderTransferFee.epoch ?? epochInfo?.epoch ?? 0),
maximumFee: BigInt(mintInfo.extensions.feeConfig.olderTransferFee.maximumFee),
transferFeeBasisPoints: mintInfo.extensions.feeConfig.olderTransferFee.transferFeeBasisPoints,
},
newerTransferFee: {
epoch: BigInt(mintInfo.extensions.feeConfig.newerTransferFee.epoch ?? epochInfo?.epoch ?? 0),
maximumFee: BigInt(mintInfo.extensions.feeConfig.newerTransferFee.maximumFee),
transferFeeBasisPoints: mintInfo.extensions.feeConfig.newerTransferFee.transferFeeBasisPoints,
},
}
: undefined;
const launchBuyAmount = fixClmmOut
? inputAmount
: (clmmComputeAmount as ReturnTypeComputeAmountOutFormat).minAmountOut.amount.raw;
const launchSwapInfo = Curve.buyExactIn({
poolInfo: launchPoolInfo,
amountB: launchBuyAmount,
protocolFeeRate: launchPoolInfo.configInfo.tradeFeeRate,
platformFeeRate: platformInfo.feeRate,
curveType: launchPoolInfo.configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA: launchMintTransferFeeConfig,
slot: slot ?? (await this.scope.connection.getSlot()),
});
const outAmount = launchSwapInfo.amountA.amount.sub(launchSwapInfo.amountA.fee ?? new BN(0));
const decimalAmountA = new Decimal(outAmount.toString());
const SLIPPAGE_UNIT = new BN(10000);
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.sub(new BN(slippage * 10000)).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(0, 1)
: new Decimal(1);
return {
clmmPoolData,
clmmComputeAmount: {
maxClmmAmountIn: fixClmmOut
? (clmmComputeAmount as ReturnTypeComputeAmountOutBaseOut).maxAmountIn.amount
: inputAmount,
clmmAmountOut: launchBuyAmount,
remainingAccounts: clmmComputeAmount.remainingAccounts,
},
clmmComputeInfo: clmmComputeAmount,
launchPoolInfo,
launchAuthProgramId: authProgramId,
launchMintTransferFeeConfig,
launchSwapInfo,
outAmount: launchSwapInfo.amountA.amount.sub(launchSwapInfo.amountA.fee ?? new BN(0)),
minOutAmount: new BN(decimalAmountA.mul(multiplier).toFixed(0)),
};
}
public async swapLaunchMintToClmm<T extends TxVersion>({
inputAmount,
clmmPoolId,
launchPoolId,
priceLimit,
slippage = 0.01,
shareFeeRate = new BN(0),
shareFeeReceiver,
ownerInfo = { useSOLBalance: true },
checkCreateATAOwner = false,
computeBudgetConfig,
txVersion,
}: {
inputAmount: BN;
clmmPoolId: string | PublicKey;
launchPoolId: string | PublicKey;
priceLimit?: Decimal;
slippage: number; // from 0~1
shareFeeRate?: BN;
shareFeeReceiver?: PublicKey;
ownerInfo?: {
useSOLBalance?: boolean;
feePayer?: PublicKey;
};
checkCreateATAOwner?: boolean;
computeBudgetConfig?: ComputeBudgetConfig;
txVersion: T;
}): Promise<
MakeTxData<
T,
{
routes: { mint: PublicKey; amount: BN; decimal: number }[];
outAmount: BN;
minOutAmount: BN;
}
>
> {
const feePayer = ownerInfo?.feePayer || this.scope.ownerPubKey;
const epochInfo = await this.scope.fetchEpochInfo();
const {
clmmPoolData,
clmmComputeAmount: { remainingAccounts },
launchPoolInfo,
launchAuthProgramId,
launchSwapInfo,
minLaunchOutAmount,
outAmount,
minOutAmount,
} = await this.computeLaunchToClmmAmount({
inputAmount,
clmmPoolId,
launchPoolId,
slippage,
epochInfo,
shareFeeRate,
});
const txBuilder = this.createTxBuilder(feePayer);
const tokenAccountMap: Record<string, PublicKey> = {};
const launchMintAProgram = launchPoolInfo.mintProgramFlag === 0 ? TOKEN_PROGRAM_ID : TOKEN_2022_PROGRAM_ID;
const { account: launchTokenAccountA } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: launchMintAProgram,
mint: launchPoolInfo.mintA,
notUseTokenAccount: false,
owner: this.scope.ownerPubKey,
skipCloseAccount: true,
createInfo: undefined,
associatedOnly: true,
checkCreateATAOwner,
});
if (!launchTokenAccountA)
throw new Error(`do not have launch mint(${launchPoolInfo.mintA.toString()}) token account`);
const mintBUseSol = launchPoolInfo.mintB.equals(WSOLMint);
const { account: launchTokenAccountB, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: TOKEN_PROGRAM_ID,
mint: launchPoolInfo.mintB,
notUseTokenAccount: mintBUseSol,
owner: this.scope.ownerPubKey,
skipCloseAccount: !mintBUseSol,
createInfo: {
payer: this.scope.ownerPubKey!,
amount: 0,
},
associatedOnly: false,
checkCreateATAOwner,
});
instructionParams && txBuilder.addInstruction(instructionParams);
if (!launchTokenAccountB)
throw new Error(`do not have launch mint(${launchPoolInfo.mintA.toString()}) token account`);
tokenAccountMap[launchPoolInfo.mintB.toBase58()] = launchTokenAccountB;
txBuilder.addInstruction({
instructions: [
sellExactInInstruction(
launchPoolInfo.programId,
this.scope.ownerPubKey,
launchAuthProgramId,
launchPoolInfo.configId,
launchPoolInfo.platformId,
new PublicKey(launchPoolId),
launchTokenAccountA,
launchTokenAccountB,
launchPoolInfo.vaultA,
launchPoolInfo.vaultB,
launchPoolInfo.mintA,
launchPoolInfo.mintB,
launchMintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(launchPoolInfo.programId, launchPoolInfo.platformId, launchPoolInfo.mintB).publicKey,
getPdaCreatorVault(launchPoolInfo.programId, launchPoolInfo.creator, launchPoolInfo.mintB).publicKey,
launchSwapInfo.amountA.amount.lt(inputAmount) ? launchSwapInfo.amountA.amount : inputAmount,
minLaunchOutAmount,
shareFeeRate,
shareFeeReceiver,
),
],
});
const baseIn = launchPoolInfo.mintB.toString() === clmmPoolData.poolInfo.mintA.address;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && clmmPoolData.poolInfo.mintA.address === WSOLMint.toBase58();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && clmmPoolData.poolInfo.mintB.address === WSOLMint.toBase58();
let sqrtPriceLimitX64: BN;
if (!priceLimit || priceLimit.equals(new Decimal(0))) {
sqrtPriceLimitX64 = baseIn ? MIN_SQRT_PRICE_X64.add(new BN(1)) : MAX_SQRT_PRICE_X64.sub(new BN(1));
} else {
sqrtPriceLimitX64 = SqrtPriceMath.priceToSqrtPriceX64(
priceLimit,
clmmPoolData.poolInfo.mintA.decimals,
clmmPoolData.poolInfo.mintB.decimals,
);
}
const [clmmMintA, clmmMintB] = [
new PublicKey(clmmPoolData.poolInfo.mintA.address),
new PublicKey(clmmPoolData.poolInfo.mintB.address),
];
const [clmmMintAProgram, clmmMintBProgram] = [
new PublicKey(clmmPoolData.poolInfo.mintA.programId),
new PublicKey(clmmPoolData.poolInfo.mintB.programId),
];
let ownerTokenAccountA = mintAUseSOLBalance
? undefined
: this.scope.account.getAssociatedTokenAccount(clmmMintA, clmmMintAProgram);
let ownerTokenAccountB = mintBUseSOLBalance
? undefined
: this.scope.account.getAssociatedTokenAccount(clmmMintB, clmmMintBProgram);
// this means mintA is wsol
if (!ownerTokenAccountA) {
const { account, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: clmmMintAProgram,
mint: clmmMintA,
notUseTokenAccount: true,
owner: this.scope.ownerPubKey,
skipCloseAccount: false,
createInfo: {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: baseIn ? inputAmount : 0,
},
associatedOnly: false,
checkCreateATAOwner,
});
ownerTokenAccountA = account!;
instructionParams && txBuilder.addInstruction(instructionParams);
}
// this means mintB is wsol
if (!ownerTokenAccountB) {
const { account, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: clmmMintBProgram,
mint: clmmMintB,
notUseTokenAccount: true,
owner: this.scope.ownerPubKey,
skipCloseAccount: false,
createInfo: {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: baseIn ? 0 : inputAmount,
},
associatedOnly: false,
checkCreateATAOwner,
});
ownerTokenAccountB = account!;
instructionParams && txBuilder.addInstruction(instructionParams);
}
tokenAccountMap[clmmPoolData.poolInfo.mintA.address] = ownerTokenAccountA;
tokenAccountMap[clmmPoolData.poolInfo.mintB.address] = ownerTokenAccountB;
if (!ownerTokenAccountA || !ownerTokenAccountB)
this.logAndCreateError("user do not have token account", {
ownerTokenAccountA,
ownerTokenAccountB,
});
txBuilder.addInstruction(
ClmmInstrument.makeSwapBaseInInstructions({
poolInfo: clmmPoolData.poolInfo,
poolKeys: clmmPoolData.poolKeys,
observationId: clmmPoolData.computePoolInfo.observationId,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
inputMint: new PublicKey(clmmPoolData.poolKeys[baseIn ? "mintA" : "mintB"].address),
amountIn: minLaunchOutAmount,
amountOutMin: minOutAmount,
sqrtPriceLimitX64,
remainingAccounts,
}),
);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
return txBuilder.versionBuild({
txVersion,
extInfo: {
routes: [
{
mint: launchPoolInfo.mintA,
amount: inputAmount,
decimal: launchPoolInfo.mintDecimalsA,
},
{
mint: launchPoolInfo.mintB,
amount: minLaunchOutAmount,
decimal: launchPoolInfo.mintDecimalsB,
},
{
mint: new PublicKey(clmmPoolData.poolKeys[baseIn ? "mintB" : "mintA"].address),
amount: outAmount,
decimal: clmmPoolData.poolKeys[baseIn ? "mintB" : "mintA"].decimals,
},
],
outAmount,
minOutAmount,
},
}) as Promise<
MakeTxData<
T,
{
routes: { mint: PublicKey; amount: BN; decimal: number }[];
outAmount: BN;
minOutAmount: BN;
}
>
>;
}
public async computeLaunchToClmmAmount({
inputAmount,
clmmPoolId,
launchPoolId,
slippage: propsSlippage,
epochInfo,
shareFeeRate = new BN(0),
clmmPoolData: propsClmmPoolData,
launchPoolInfo: propsLaunchPoolInfo,
launchPlatformInfo: propsLaunchPlatformInfo,
}: {
clmmPoolId: string | PublicKey;
launchPoolId: string | PublicKey;
inputAmount: BN;
slippage: number;
epochInfo?: EpochInfo;
shareFeeRate?: BN;
clmmPoolData?: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
computePoolInfo: ComputeClmmPoolInfo;
tickData: ReturnTypeFetchMultiplePoolTickArrays;
};
launchPoolInfo?: LaunchpadPoolInfo & { programId: PublicKey; configInfo: LaunchpadConfigInfo };
launchPlatformInfo?: LaunchpadPlatformInfo;
}): Promise<{
clmmPoolData: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
computePoolInfo: ComputeClmmPoolInfo;
tickData: ReturnTypeFetchMultiplePoolTickArrays;
};
clmmComputeAmount: ReturnTypeComputeAmountOutFormat;
launchPoolInfo: LaunchpadPoolInfo & { programId: PublicKey; configInfo: LaunchpadConfigInfo };
launchAuthProgramId: PublicKey;
minLaunchOutAmount: BN;
outAmount: BN;
minOutAmount: BN;
launchSwapInfo: SwapInfoReturn;
launchMintTransferFeeConfig?: TransferFeeConfig;
}> {
// split slippage for clmm swap and launch buy
const slippage =
propsSlippage > 0
? new Decimal(propsSlippage).div(2).toDecimalPlaces(4, Decimal.ROUND_DOWN).toNumber()
: propsSlippage;
let launchPoolInfo = propsLaunchPoolInfo;
if (!launchPoolInfo)
launchPoolInfo = await this.scope.launchpad.getRpcPoolInfo({ poolId: new PublicKey(launchPoolId) });
const inputMint = launchPoolInfo.mintB;
const clmmPoolData = propsClmmPoolData ?? (await this.scope.clmm.getPoolInfoFromRpc(clmmPoolId.toString()));
if (
inputMint.toString() !== clmmPoolData.poolInfo.mintA.address &&
inputMint.toString() !== clmmPoolData.poolInfo.mintB.address
)
throw new Error("input mint does not match clmm pool mints, please check");
const baseIn = inputMint.toString() === clmmPoolData.poolInfo.mintA.address;
const tokenOut = clmmPoolData.poolInfo[baseIn ? "mintB" : "mintA"];
let platformInfo = propsLaunchPlatformInfo;
if (!platformInfo) {
const data = await this.scope.connection.getAccountInfo(launchPoolInfo.platformId);
platformInfo = PlatformConfig.decode(data!.data);
}
const mintInfo = await this.scope.token.getTokenInfo(launchPoolInfo.mintA);
const authProgramId = getPdaLaunchpadAuth(launchPoolInfo.programId).publicKey;
const launchMintTransferFeeConfig = mintInfo.extensions.feeConfig
? {
transferFeeConfigAuthority: PublicKey.default,
withdrawWithheldAuthority: PublicKey.default,
withheldAmount: BigInt(0),
olderTransferFee: {
epoch: BigInt(mintInfo.extensions.feeConfig.olderTransferFee.epoch ?? epochInfo?.epoch ?? 0),
maximumFee: BigInt(mintInfo.extensions.feeConfig.olderTransferFee.maximumFee),
transferFeeBasisPoints: mintInfo.extensions.feeConfig.olderTransferFee.transferFeeBasisPoints,
},
newerTransferFee: {
epoch: BigInt(mintInfo.extensions.feeConfig.newerTransferFee.epoch ?? epochInfo?.epoch ?? 0),
maximumFee: BigInt(mintInfo.extensions.feeConfig.newerTransferFee.maximumFee),
transferFeeBasisPoints: mintInfo.extensions.feeConfig.newerTransferFee.transferFeeBasisPoints,
},
}
: undefined;
const launchSwapInfo = Curve.sellExactIn({
poolInfo: launchPoolInfo,
amountA: inputAmount,
protocolFeeRate: launchPoolInfo.configInfo.tradeFeeRate,
platformFeeRate: platformInfo.feeRate,
curveType: launchPoolInfo.configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA: launchMintTransferFeeConfig,
slot: await this.scope.connection.getSlot(),
});
const outAmount = launchSwapInfo.amountB;
const decimalAmountB = new Decimal(outAmount.toString());
const SLIPPAGE_UNIT = new BN(10000);
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.sub(new BN(slippage * 10000)).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(0, 1)
: new Decimal(1);
const minLaunchOutAmount = new BN(decimalAmountB.mul(multiplier).toFixed(0));
const clmmComputeAmount = await PoolUtils.computeAmountOutFormat({
poolInfo: clmmPoolData.computePoolInfo,
tickArrayCache: clmmPoolData.tickData[clmmPoolId.toString()],
amountIn: minLaunchOutAmount,
tokenOut,
slippage,
epochInfo: epochInfo ?? (await this.scope.fetchEpochInfo()),
});
return {
clmmPoolData,
clmmComputeAmount,
launchPoolInfo,
launchAuthProgramId: authProgramId,
launchMintTransferFeeConfig,
launchSwapInfo,
minLaunchOutAmount,
outAmount: clmmComputeAmount.amountOut.amount.raw,
minOutAmount: clmmComputeAmount.minAmountOut.amount.raw,
};
}
// get all amm/clmm/cpmm pools data only with id and mint
public async fetchRoutePoolBasicInfo(programIds?: { amm: PublicKey; clmm: PublicKey; cpmm: PublicKey }): Promise<{
ammPools: BasicPoolInfo[];
clmmPools: BasicPoolInfo[];
cpmmPools: BasicPoolInfo[];
}> {
const { amm = AMM_V4, clmm = CLMM_PROGRAM_ID, cpmm = CREATE_CPMM_POOL_PROGRAM } = programIds || {};
const ammPoolsData = await this.scope.connection.getProgramAccounts(amm, {
dataSlice: { offset: liquidityStateV4Layout.offsetOf("baseMint"), length: 64 },
});
const layoutAmm = struct([publicKey("baseMint"), publicKey("quoteMint")]);
const ammData = ammPoolsData.map((data) => ({
id: data.pubkey,
version: 4,
mintA: layoutAmm.decode(data.account.data).baseMint,
mintB: layoutAmm.decode(data.account.data).quoteMint,
}));
const layout = struct([publicKey("mintA"), publicKey("mintB")]);
const clmmPoolsData = await this.scope.connection.getProgramAccounts(clmm, {
filters: [{ dataSize: PoolInfoLayout.span }],
dataSlice: { offset: PoolInfoLayout.offsetOf("mintA"), length: 64 },
});
const clmmData = clmmPoolsData.map((data) => {
const clmm = layout.decode(data.account.data);
return {
id: data.pubkey,
version: 6,
mintA: clmm.mintA,
mintB: clmm.mintB,
};
});
const cpmmPools = await this.scope.connection.getProgramAccounts(cpmm, {
dataSlice: { offset: CpmmPoolInfoLayout.offsetOf("mintA"), length: 64 },
});
const cpmmData = cpmmPools.map((data) => {
const clmm = layout.decode(data.account.data);
return {
id: data.pubkey,
version: 7,
mintA: clmm.mintA,
mintB: clmm.mintB,
};
});
return {
clmmPools: clmmData,
ammPools: ammData,
cpmmPools: cpmmData,
};
}
// get pools with in routes
public getAllRoute({
inputMint,
outputMint,
clmmPools,
ammPools,
cpmmPools,
}: {
inputMint: PublicKey;
outputMint: PublicKey;
clmmPools: BasicPoolInfo[];
ammPools: BasicPoolInfo[];
cpmmPools: BasicPoolInfo[];
}): ReturnTypeGetAllRoute {
inputMint = inputMint.toString() === PublicKey.default.toString() ? WSOLMint : inputMint;
outputMint = outputMint.toString() === PublicKey.default.toString() ? WSOLMint : outputMint;
const needSimulate: { [poolKey: string]: BasicPoolInfo } = {};
const needTickArray: { [poolKey: string]: BasicPoolInfo } = {};
const cpmmPoolList: { [poolKey: string]: BasicPoolInfo } = {};
const directPath: BasicPoolInfo[] = [];
const routePathDict: RoutePathType = {}; // {[route mint: string]: {in: [] , out: []}}
for (const itemClmmPool of clmmPools ?? []) {
if (
(itemClmmPool.mintA.equals(inputMint) && itemClmmPool.mintB.equals(outputMint)) ||
(itemClmmPool.mintA.equals(outputMint) && itemClmmPool.mintB.equals(inputMint))
) {
directPath.push(itemClmmPool);
needTickArray[itemClmmPool.id.toString()] = itemClmmPool;
}
if (itemClmmPool.mintA.equals(inputMint)) {
const t = itemClmmPool.mintB.toString();
if (routePathDict[t] === undefined)
routePathDict[t] = {
mintProgram: TOKEN_PROGRAM_ID, // to fetch later
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[t].in.push(itemClmmPool);
}
if (itemClmmPool.mintB.equals(inputMint)) {
const t = itemClmmPool.mintA.toString();
if (routePathDict[t] === undefined)
routePathDict[t] = {
mintProgram: TOKEN_PROGRAM_ID, // to fetch later
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[t].in.push(itemClmmPool);
}
if (itemClmmPool.mintA.equals(outputMint)) {
const t = itemClmmPool.mintB.toString();
if (routePathDict[t] === undefined)
routePathDict[t] = {
mintProgram: TOKEN_PROGRAM_ID, // to fetch later
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[t].out.push(itemClmmPool);
}
if (itemClmmPool.mintB.equals(outputMint)) {
const t = itemClmmPool.mintA.toString();
if (routePathDict[t] === undefined)
routePathDict[t] = {
mintProgram: TOKEN_PROGRAM_ID, // to fetch later
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[t].out.push(itemClmmPool);
}
}
const addLiquidityPools: BasicPoolInfo[] = [];
for (const itemAmmPool of ammPools) {
if (
(itemAmmPool.mintA.equals(inputMint) && itemAmmPool.mintB.equals(outputMint)) ||
(itemAmmPool.mintA.equals(outputMint) && itemAmmPool.mintB.equals(inputMint))
) {
directPath.push(itemAmmPool);
needSimulate[itemAmmPool.id.toBase58()] = itemAmmPool;
addLiquidityPools.push(itemAmmPool);
}
if (itemAmmPool.mintA.equals(inputMint)) {
if (routePathDict[itemAmmPool.mintB.toBase58()] === undefined)
routePathDict[itemAmmPool.mintB.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemAmmPool.mintB.toBase58()].in.push(itemAmmPool);
}
if (itemAmmPool.mintB.equals(inputMint)) {
if (routePathDict[itemAmmPool.mintA.toBase58()] === undefined)
routePathDict[itemAmmPool.mintA.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemAmmPool.mintA.toBase58()].in.push(itemAmmPool);
}
if (itemAmmPool.mintA.equals(outputMint)) {
if (routePathDict[itemAmmPool.mintB.toBase58()] === undefined)
routePathDict[itemAmmPool.mintB.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemAmmPool.mintB.toBase58()].out.push(itemAmmPool);
}
if (itemAmmPool.mintB.equals(outputMint)) {
if (routePathDict[itemAmmPool.mintA.toBase58()] === undefined)
routePathDict[itemAmmPool.mintA.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemAmmPool.mintA.toBase58()].out.push(itemAmmPool);
}
}
for (const itemCpmmPool of cpmmPools) {
if (
(itemCpmmPool.mintA.equals(inputMint) && itemCpmmPool.mintB.equals(outputMint)) ||
(itemCpmmPool.mintA.equals(outputMint) && itemCpmmPool.mintB.equals(inputMint))
) {
directPath.push(itemCpmmPool);
cpmmPoolList[itemCpmmPool.id.toBase58()] = itemCpmmPool;
}
if (itemCpmmPool.mintA.equals(inputMint)) {
if (routePathDict[itemCpmmPool.mintB.toBase58()] === undefined)
routePathDict[itemCpmmPool.mintB.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemCpmmPool.mintB.toBase58()].in.push(itemCpmmPool);
}
if (itemCpmmPool.mintB.equals(inputMint)) {
if (routePathDict[itemCpmmPool.mintA.toBase58()] === undefined)
routePathDict[itemCpmmPool.mintA.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemCpmmPool.mintA.toBase58()].in.push(itemCpmmPool);
}
if (itemCpmmPool.mintA.equals(outputMint)) {
if (routePathDict[itemCpmmPool.mintB.toBase58()] === undefined)
routePathDict[itemCpmmPool.mintB.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemCpmmPool.mintB.toBase58()].out.push(itemCpmmPool);
}
if (itemCpmmPool.mintB.equals(outputMint)) {
if (routePathDict[itemCpmmPool.mintA.toBase58()] === undefined)
routePathDict[itemCpmmPool.mintA.toBase58()] = {
mintProgram: TOKEN_PROGRAM_ID,
in: [],
out: [],
mDecimals: 0, // to fetch later
};
routePathDict[itemCpmmPool.mintA.toBase58()].out.push(itemCpmmPool);
}
}
for (const t of Object.keys(routePathDict)) {
if (
routePathDict[t].in.length === 1 &&
routePathDict[t].out.length === 1 &&
routePathDict[t].in[0].id.equals(routePathDict[t].out[0].id)
) {
delete routePathD