@raydium-io/raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
1,533 lines (1,360 loc) • 63.9 kB
text/typescript
import ModuleBase, { ModuleBaseProps } from "../moduleBase";
import {
TxVersion,
MakeTxData,
LAUNCHPAD_PROGRAM,
getMultipleAccountsInfoWithCustomFlags,
getATAAddress,
MakeMultiTxData,
} from "@/common";
import {
BuyToken,
BuyTokenExactOut,
ClaimAllPlatformFee,
ClaimCreatorFee,
ClaimMultiCreatorFee,
ClaimMultipleVaultPlatformFee,
ClaimMultiVesting,
ClaimPlatformFee,
ClaimVaultPlatformFee,
ClaimVesting,
CpmmCreatorFeeOn,
CreateLaunchPad,
CreateMultipleVesting,
CreatePlatform,
CreatePlatformVestingAccount,
CreateVesting,
LaunchpadConfigInfo,
LaunchpadPoolInfo,
SellToken,
SellTokenExactOut,
UpdatePlatform,
} from "./type";
import {
getPdaCreatorFeeVaultAuth,
getPdaCreatorVault,
getPdaLaunchpadAuth,
getPdaLaunchpadPoolId,
getPdaLaunchpadVaultId,
getPdaPlatformConfigAccess,
getPdaPlatformFeeVaultAuth,
getPdaPlatformId,
getPdaPlatformVault,
getPdaVestId,
} from "./pda";
import {
buyExactInInstruction,
sellExactInInstruction,
createPlatformConfig,
updatePlatformConfig,
claimPlatformFee,
createVestingAccount,
claimVestedToken,
buyExactOutInstruction,
initializeWithToken2022,
sellExactOut,
claimPlatformFeeFromVault,
claimCreatorFee,
initializeV2,
createPlatformVestingAccountIns,
} from "./instrument";
import {
NATIVE_MINT,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
TransferFeeConfig,
createAssociatedTokenAccountIdempotentInstruction,
createSyncNativeInstruction,
getTransferFeeConfig,
unpackMint,
} from "@solana/spl-token";
import BN from "bn.js";
import { PublicKey, SystemProgram } from "@solana/web3.js";
import { getPdaMetadataKey } from "../clmm";
import { LaunchpadConfig, LaunchpadPool, PlatformConfig } from "./layout";
import { Curve, SwapInfoReturn } from "./curve/curve";
import Decimal from "decimal.js";
import { ApiV3Token } from "@/api";
export const LaunchpadPoolInitParam = {
initPriceX64: new BN("515752397214619"),
supply: new BN(1_000_000_000_000_000),
totalSellA: new BN(793_100_000_000_000),
totalFundRaisingB: new BN(85_000_000_000),
totalFundRaisingBUSD: new BN(12_500_000_000),
totalLockedAmount: new BN("0"),
cliffPeriod: new BN("0"),
unlockPeriod: new BN("0"),
decimals: 6,
virtualA: new BN("1073471847374405"),
virtualB: new BN("30050573465"),
realA: new BN(0),
realB: new BN(0),
protocolFee: new BN(0),
platformId: new PublicKey("4Bu96XjU84XjPDSpveTVf6LYGCkfW5FK7SNkREWcEfV4"),
vestingSchedule: {
totalLockedAmount: new BN(0),
cliffPeriod: new BN(0),
unlockPeriod: new BN(0),
startTime: new BN(0),
totalAllocatedShare: new BN(0),
},
};
const SLIPPAGE_UNIT = new BN(10000);
export const usdMintBSet = new Set([
"USDCoctVLVnvTXBEuP9s8hntucdJokbo17RwHuNXemT",
"USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB",
]);
export interface SwapInfoReturnExt extends SwapInfoReturn {
decimalOutAmount: Decimal;
minDecimalOutAmount: Decimal;
}
export default class LaunchpadModule extends ModuleBase {
constructor(params: ModuleBaseProps) {
super(params);
}
public async createLaunchpad<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
platformId = LaunchpadPoolInitParam.platformId,
mintA,
decimals = 6,
mintBDecimals = 9,
name,
symbol,
uri,
migrateType,
configId,
configInfo: propConfigInfo,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
buyAmount,
minMintAAmount,
slippage,
associatedOnly = true,
checkCreateATAOwner = false,
extraSigners,
token2022,
transferFeeExtensionParams,
creatorFeeOn = CpmmCreatorFeeOn.OnlyTokenB,
platformConfigAccess,
...extraConfigs
}: CreateLaunchPad<T>): Promise<
MakeMultiTxData<T, { address: LaunchpadPoolInfo & { poolId: PublicKey }; swapInfo: SwapInfoReturnExt }>
> {
const txBuilder = this.createTxBuilder(feePayer);
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
token2022 = !!transferFeeExtensionParams;
if (token2022) migrateType = "cpmm";
let configInfo = propConfigInfo;
if (!configInfo && configId) {
const r = await this.scope.connection.getAccountInfo(configId);
if (r) configInfo = LaunchpadConfig.decode(r.data);
}
if (!configInfo) this.logAndCreateError("config not found");
const mintB = configInfo!.mintB;
const curType = configInfo!.curveType;
// const { publicKey: configId } = getPdaLaunchpadConfigId(programId, mintB, curType, configIndex);
const { publicKey: poolId } = getPdaLaunchpadPoolId(programId, mintA, mintB);
const { publicKey: vaultA } = getPdaLaunchpadVaultId(programId, poolId, mintA);
const { publicKey: vaultB } = getPdaLaunchpadVaultId(programId, poolId, mintB);
const { publicKey: metaId } = getPdaMetadataKey(mintA);
this.logDebug(
`create token: ${mintA.toBase58()}, mintB: ${mintB.toBase58()}, decimals A:${decimals}/B:${mintBDecimals}, config:${configId.toBase58()}`,
);
if (symbol.length > 10) this.logAndCreateError("Symbol length should shorter than 11");
if (!uri) this.logAndCreateError("uri should not empty");
const configs = await this.scope.api.fetchLaunchConfigs();
const apiConfig = configs.find((c) => c.key.pubKey === configId.toBase58())!;
const supply = extraConfigs?.supply ?? new BN(apiConfig.defaultParams.supplyInit);
const totalSellA = extraConfigs?.totalSellA ?? new BN(apiConfig.defaultParams.totalSellA);
const totalFundRaisingB = extraConfigs?.totalFundRaisingB ?? new BN(apiConfig.defaultParams.totalFundRaisingB);
const totalLockedAmount = extraConfigs?.totalLockedAmount ?? new BN(0);
// let defaultPlatformFeeRate = platformFeeRate;
// let defaultPlatformVestingScale = platformVestingScale;
// if (!platformFeeRate) {
// const platformData = await this.scope.connection.getAccountInfo(platformId);
// if (!platformData) this.logAndCreateError("platform id not found:", platformId.toString());
// const platform = PlatformConfig.decode(platformData!.data);
// defaultPlatformVestingScale = platform.platformVestingScale;
// defaultPlatformFeeRate = platform.feeRate;
// }
const platformData = await this.scope.connection.getAccountInfo(platformId);
if (!platformData) this.logAndCreateError("platform id not found:", platformId.toString());
const platform = PlatformConfig.decode(platformData!.data);
const defaultPlatformVestingScale = platform.platformVestingScale;
const defaultPlatformFeeRate = platform.feeRate;
const curve = Curve.getCurve(configInfo!.curveType);
const initParam = curve.getInitParam({
supply,
totalFundRaising: totalFundRaisingB,
totalSell: totalSellA,
totalLockedAmount,
migrateFee: configInfo!.migrateFee,
});
let mintBInfo: ApiV3Token | undefined;
try {
mintBInfo = await this.scope.token.getTokenInfo(mintB);
} catch {
this.logDebug("can not get mintB info from getTokenInfo");
}
const poolInfo: LaunchpadPoolInfo = {
epoch: new BN(896),
bump: 254,
status: 0,
mintDecimalsA: decimals,
mintDecimalsB: mintBInfo?.decimals ?? mintBDecimals,
supply,
totalSellA,
mintA: new PublicKey(mintA),
mintB,
virtualA: initParam.a,
virtualB: initParam.b,
realA: LaunchpadPoolInitParam.realA,
realB: LaunchpadPoolInitParam.realB,
migrateFee: configInfo!.migrateFee,
migrateType: migrateType === "amm" ? 0 : 1,
protocolFee: LaunchpadPoolInitParam.protocolFee,
platformFee: defaultPlatformFeeRate!,
platformId,
configId,
vaultA,
vaultB,
creator: this.scope.ownerPubKey,
totalFundRaisingB,
vestingSchedule: {
totalLockedAmount,
cliffPeriod: new BN(0),
unlockPeriod: new BN(0),
startTime: new BN(0),
totalAllocatedShare: new BN(0),
},
mintProgramFlag: token2022 ? 1 : 0,
cpmmCreatorFeeOn: creatorFeeOn,
platformVestingShare: defaultPlatformVestingScale ?? new BN(0),
};
const initCurve = Curve.getCurve(configInfo!.curveType);
const { c } = initCurve.getInitParam({
supply: poolInfo.supply,
totalFundRaising: poolInfo.totalFundRaisingB,
totalLockedAmount,
totalSell: configInfo!.curveType === 0 ? poolInfo.totalSellA : new BN(0),
migrateFee: configInfo!.migrateFee,
});
try {
Curve.checkParam({
supply: poolInfo.supply,
totalFundRaising: poolInfo.totalFundRaisingB,
totalSell: c,
totalLockedAmount,
decimals: poolInfo.mintDecimalsA,
config: configInfo!,
migrateType,
});
this.logDebug("check init params success");
} catch (e: any) {
this.logAndCreateError(`check create mint params failed, ${e.message}`);
}
txBuilder.addInstruction({
instructions: [
token2022
? initializeWithToken2022(
programId,
feePayer ?? this.scope.ownerPubKey,
this.scope.ownerPubKey,
configId,
platformId,
authProgramId,
poolId,
mintA,
mintB,
vaultA,
vaultB,
decimals,
name,
symbol,
uri || "https://",
{
type:
curType === 0
? "ConstantCurve"
: curType === 1
? "FixedCurve"
: curType === 2
? "LinearCurve"
: "ConstantCurve",
totalSellA,
migrateType,
supply,
totalFundRaisingB,
},
totalLockedAmount,
extraConfigs?.cliffPeriod ?? new BN(0),
extraConfigs?.unlockPeriod ?? new BN(0),
creatorFeeOn,
transferFeeExtensionParams,
)
: initializeV2(
programId,
feePayer ?? this.scope.ownerPubKey,
this.scope.ownerPubKey,
configId,
platformId,
authProgramId,
poolId,
mintA,
mintB,
vaultA,
vaultB,
metaId,
decimals,
name,
symbol,
uri || "https://",
{
type:
curType === 0
? "ConstantCurve"
: curType === 1
? "FixedCurve"
: curType === 2
? "LinearCurve"
: "ConstantCurve",
totalSellA,
migrateType,
supply,
totalFundRaisingB,
},
totalLockedAmount,
extraConfigs?.cliffPeriod ?? new BN(0),
extraConfigs?.unlockPeriod ?? new BN(0),
creatorFeeOn,
platformConfigAccess ? getPdaPlatformConfigAccess(programId, platformId, configId).publicKey : undefined,
),
],
});
const epoch = token2022 ? await this.scope.connection.getEpochInfo() : undefined;
const fee = transferFeeExtensionParams
? {
epoch: BigInt(epoch?.epoch || 0),
maximumFee: BigInt(transferFeeExtensionParams?.maxinumFee.toString() ?? 0),
transferFeeBasisPoints: transferFeeExtensionParams?.transferFeeBasePoints ?? 0,
}
: undefined;
let swapInfo: SwapInfoReturn = {
amountA: {
amount: new BN(0),
fee: undefined,
expirationTime: undefined,
},
amountB: new BN(0),
splitFee: {
platformFee: new BN(0),
shareFee: new BN(0),
protocolFee: new BN(0),
creatorFee: new BN(0),
},
};
let splitIns;
if (extraSigners?.length) txBuilder.addInstruction({ signers: extraSigners });
if (!extraConfigs.createOnly) {
const { builder, extInfo } = await this.buyToken({
programId,
authProgramId,
mintAProgram: token2022 ? TOKEN_2022_PROGRAM_ID : undefined,
mintA,
mintB,
poolInfo,
buyAmount,
minMintAAmount,
shareFeeRate: extraConfigs.shareFeeRate,
shareFeeReceiver: extraConfigs.shareFeeReceiver,
configInfo,
platformFeeRate: defaultPlatformFeeRate,
slippage,
associatedOnly,
checkCreateATAOwner,
skipCheckMintA: !fee,
transferFeeConfigA: fee
? {
transferFeeConfigAuthority: authProgramId,
withdrawWithheldAuthority: authProgramId,
withheldAmount: BigInt(0),
olderTransferFee: fee,
newerTransferFee: fee,
}
: undefined,
fromCreate: true,
});
txBuilder.addInstruction({ ...builder.AllTxData });
swapInfo = { ...extInfo };
splitIns =
(this.scope.cluster === "devnet" || txVersion === TxVersion.LEGACY) && extraConfigs.shareFeeReceiver
? [builder.allInstructions[0]]
: undefined;
}
txBuilder.addTipInstruction(txTipConfig);
if (txVersion === TxVersion.V0)
return txBuilder.sizeCheckBuildV0({
computeBudgetConfig,
swapInfo,
splitIns,
address: {
...poolInfo,
poolId,
},
}) as Promise<
MakeMultiTxData<T, { address: LaunchpadPoolInfo & { poolId: PublicKey }; swapInfo: SwapInfoReturnExt }>
>;
return txBuilder.sizeCheckBuild({
computeBudgetConfig,
swapInfo,
splitIns,
address: {
...poolInfo,
poolId,
},
}) as Promise<
MakeMultiTxData<T, { address: LaunchpadPoolInfo & { poolId: PublicKey }; swapInfo: SwapInfoReturnExt }>
>;
}
public async buyToken<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
mintA,
mintAProgram = TOKEN_PROGRAM_ID,
mintB = NATIVE_MINT,
poolInfo: propPoolInfo,
configInfo: propConfigInfo,
platformFeeRate,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
buyAmount,
minMintAAmount: propMinMintAAmount,
slippage,
shareFeeRate = new BN(0),
shareFeeReceiver,
associatedOnly = true,
checkCreateATAOwner = false,
fromCreate = false,
transferFeeConfigA: propsTransferFeeConfigA,
skipCheckMintA = false,
}: BuyToken<T>): Promise<MakeTxData<T, SwapInfoReturnExt>> {
if (buyAmount.lte(new BN(0))) this.logAndCreateError("buy amount should gt 0:", buyAmount.toString());
const txBuilder = this.createTxBuilder(feePayer);
const { publicKey: poolId } = getPdaLaunchpadPoolId(programId, mintA, mintB);
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
let transferFeeConfigA = propsTransferFeeConfigA;
if (!skipCheckMintA) {
if (!transferFeeConfigA) {
const mintInfo = await this.scope.connection.getAccountInfo(mintA);
if (mintInfo && mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
mintAProgram = mintInfo.owner;
const onlineData = unpackMint(mintA, mintInfo, mintAProgram);
transferFeeConfigA = getTransferFeeConfig(onlineData) || undefined;
}
} else {
mintAProgram = TOKEN_2022_PROGRAM_ID;
}
}
const userTokenAccountA = this.scope.account.getAssociatedTokenAccount(mintA, mintAProgram);
const isMintBSol = mintB.equals(NATIVE_MINT);
const useAta = fromCreate && isMintBSol;
let userTokenAccountB: PublicKey | null = useAta
? this.scope.account.getAssociatedTokenAccount(mintB, TOKEN_PROGRAM_ID)
: null;
const mintBUseSOLBalance = isMintBSol;
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
userTokenAccountA,
this.scope.ownerPubKey,
mintA,
mintAProgram,
),
...(useAta
? [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
userTokenAccountB!,
this.scope.ownerPubKey,
mintB,
TOKEN_PROGRAM_ID,
),
SystemProgram.transfer({
fromPubkey: this.scope.ownerPubKey,
toPubkey: userTokenAccountB!,
lamports: BigInt(buyAmount.toString()),
}),
createSyncNativeInstruction(userTokenAccountB!),
]
: []),
],
});
if (!useAta) {
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
mint: mintB,
owner: this.scope.ownerPubKey,
createInfo: mintBUseSOLBalance
? {
payer: this.scope.ownerPubKey!,
amount: buyAmount,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) userTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
}
if (!userTokenAccountB)
this.logAndCreateError(
`cannot found mintB(${mintB.toBase58()}) buy token accounts`,
"tokenAccounts",
this.scope.account.tokenAccounts,
);
let poolInfo = propPoolInfo;
if (!poolInfo) {
const poolData = await this.scope.connection.getAccountInfo(poolId, { commitment: "processed" });
if (!poolData) this.logAndCreateError("cannot found pool:", poolId.toBase58());
poolInfo = LaunchpadPool.decode(poolData!.data);
}
let configInfo = propConfigInfo;
const allData = await getMultipleAccountsInfoWithCustomFlags(
this.scope.connection,
[configInfo ? undefined : poolInfo.configId, poolInfo.platformId]
.filter(Boolean)
.map((key) => ({ pubkey: key! })),
);
if (!configInfo) {
const data = allData.find((d) => d.pubkey.equals(poolInfo!.configId));
if (!data || !data.accountInfo) this.logAndCreateError("config not found: ", poolInfo.configId.toBase58());
configInfo = LaunchpadConfig.decode(data!.accountInfo!.data);
}
const platformData = allData.find((d) => d.pubkey.equals(poolInfo!.platformId));
if (!platformData || !platformData.accountInfo)
this.logAndCreateError("platform info not found: ", poolInfo.configId.toBase58());
const platformInfo = PlatformConfig.decode(platformData!.accountInfo!.data);
platformFeeRate = platformFeeRate || platformInfo.feeRate;
const calculatedAmount = Curve.buyExactIn({
poolInfo,
amountB: buyAmount,
protocolFeeRate: configInfo.tradeFeeRate,
platformFeeRate,
curveType: configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA,
slot: await this.scope.connection.getSlot(),
});
const decimalAmountA = new Decimal(calculatedAmount.amountA.amount.toString()).sub(
calculatedAmount.amountA.fee?.toString() ?? 0,
);
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.sub(slippage).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(0, 1)
: new Decimal(1);
const minMintAAmount =
propMinMintAAmount ??
(slippage
? new BN(decimalAmountA.mul(multiplier).toFixed(0))
: calculatedAmount.amountA.amount.sub(calculatedAmount.amountA.fee ?? new BN(0)));
if (calculatedAmount.amountB.lt(buyAmount)) {
console.log(
`maximum ${mintA.toBase58()} amount can buy is ${calculatedAmount.amountA.toString()}, input ${mintB.toBase58()} amount: ${calculatedAmount.amountB.toString()}`,
);
}
const shareATA = shareFeeReceiver ? getATAAddress(shareFeeReceiver, mintB, TOKEN_PROGRAM_ID).publicKey : undefined;
if (shareATA) {
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(this.scope.ownerPubKey, shareATA, shareFeeReceiver!, mintB),
],
});
}
txBuilder.addInstruction({
instructions: [
buyExactInInstruction(
programId,
this.scope.ownerPubKey,
authProgramId,
poolInfo.configId,
poolInfo.platformId,
poolId,
userTokenAccountA!,
userTokenAccountB!,
poolInfo.vaultA,
poolInfo.vaultB,
mintA,
mintB,
mintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(programId, poolInfo.platformId, mintB).publicKey,
getPdaCreatorVault(programId, poolInfo.creator, mintB).publicKey,
calculatedAmount.amountB.lt(buyAmount) ? calculatedAmount.amountB : buyAmount,
minMintAAmount,
shareFeeRate,
shareATA,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<SwapInfoReturnExt>({
txVersion,
extInfo: {
...calculatedAmount,
decimalOutAmount: decimalAmountA,
minDecimalOutAmount: new Decimal(minMintAAmount.toString()),
},
}) as Promise<MakeTxData<T, SwapInfoReturnExt>>;
}
public async buyTokenExactOut<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
mintA,
mintAProgram = TOKEN_PROGRAM_ID,
mintB = NATIVE_MINT,
poolInfo: propPoolInfo,
configInfo: propConfigInfo,
transferFeeConfigA: propsTransferFeeConfigA,
platformFeeRate,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
maxBuyAmount,
outAmount,
slippage,
shareFeeRate = new BN(0),
shareFeeReceiver,
associatedOnly = true,
checkCreateATAOwner = false,
skipCheckMintA = false,
}: BuyTokenExactOut<T>): Promise<MakeTxData<T, { outAmount: BN; maxSpentAmount: BN }>> {
if (outAmount.lte(new BN(0))) this.logAndCreateError("out amount should gt 0:", outAmount.toString());
const txBuilder = this.createTxBuilder(feePayer);
const { publicKey: poolId } = getPdaLaunchpadPoolId(programId, mintA, mintB);
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
let poolInfo = propPoolInfo;
if (!poolInfo) {
const poolData = await this.scope.connection.getAccountInfo(poolId, { commitment: "processed" });
if (!poolData) this.logAndCreateError("cannot found pool:", poolId.toBase58());
poolInfo = LaunchpadPool.decode(poolData!.data);
}
let configInfo = propConfigInfo;
const allData = await getMultipleAccountsInfoWithCustomFlags(
this.scope.connection,
[configInfo ? undefined : poolInfo.configId, poolInfo.platformId]
.filter(Boolean)
.map((key) => ({ pubkey: key! })),
);
if (!configInfo) {
const data = allData.find((d) => d.pubkey.equals(poolInfo!.configId));
if (!data || !data.accountInfo) this.logAndCreateError("config not found: ", poolInfo.configId.toBase58());
configInfo = LaunchpadConfig.decode(data!.accountInfo!.data);
}
const platformData = allData.find((d) => d.pubkey.equals(poolInfo!.platformId));
if (!platformData || !platformData.accountInfo)
this.logAndCreateError("platform info not found: ", poolInfo.configId.toBase58());
const platformInfo = PlatformConfig.decode(platformData!.accountInfo!.data);
platformFeeRate = platformFeeRate || platformInfo.feeRate;
let transferFeeConfigA = propsTransferFeeConfigA;
if (!skipCheckMintA) {
if (!transferFeeConfigA) {
const mintInfo = await this.scope.connection.getAccountInfo(mintA);
if (mintInfo && mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
mintAProgram = mintInfo.owner;
const onlineData = unpackMint(mintA, mintInfo, mintAProgram);
transferFeeConfigA = getTransferFeeConfig(onlineData) || undefined;
}
} else {
mintAProgram = TOKEN_2022_PROGRAM_ID;
}
}
const calculatedAmount = Curve.buyExactOut({
poolInfo,
amountA: outAmount,
protocolFeeRate: configInfo.tradeFeeRate,
platformFeeRate,
curveType: configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA,
slot: await this.scope.connection.getSlot(),
});
const decimalAmountB = new Decimal(calculatedAmount.amountB.toString());
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.add(slippage).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(
0,
Number.MIN_SAFE_INTEGER,
)
: new Decimal(1);
const maxAmountB =
maxBuyAmount ?? slippage ? new BN(decimalAmountB.mul(multiplier).toFixed(0)) : calculatedAmount.amountB;
const userTokenAccountA = this.scope.account.getAssociatedTokenAccount(mintA, mintAProgram);
let userTokenAccountB: PublicKey | null = null;
const mintBUseSOLBalance = mintB.equals(NATIVE_MINT);
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
userTokenAccountA,
this.scope.ownerPubKey,
mintA,
mintAProgram,
),
],
});
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
mint: mintB,
owner: this.scope.ownerPubKey,
createInfo: mintBUseSOLBalance
? {
payer: this.scope.ownerPubKey!,
amount: calculatedAmount.amountB,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) userTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (userTokenAccountB === undefined)
this.logAndCreateError(
`cannot found mintB(${mintB.toBase58()}) token accounts`,
"tokenAccounts",
this.scope.account.tokenAccounts,
);
const shareATA = shareFeeReceiver ? getATAAddress(shareFeeReceiver, mintB, TOKEN_PROGRAM_ID).publicKey : undefined;
if (shareATA) {
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(this.scope.ownerPubKey, shareATA, shareFeeReceiver!, mintB),
],
});
}
txBuilder.addInstruction({
instructions: [
buyExactOutInstruction(
programId,
this.scope.ownerPubKey,
authProgramId,
poolInfo.configId,
poolInfo.platformId,
poolId,
userTokenAccountA!,
userTokenAccountB!,
poolInfo.vaultA,
poolInfo.vaultB,
mintA,
mintB,
mintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(programId, poolInfo.platformId, mintB).publicKey,
getPdaCreatorVault(programId, poolInfo.creator, mintB).publicKey,
outAmount,
maxAmountB,
shareFeeRate,
shareATA,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ outAmount: BN; maxSpentAmount: BN }>({
txVersion,
extInfo: {
maxSpentAmount: maxAmountB,
outAmount,
},
}) as Promise<MakeTxData<T, { outAmount: BN; maxSpentAmount: BN }>>;
}
public async sellToken<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
mintAProgram = TOKEN_PROGRAM_ID,
mintA,
mintB = NATIVE_MINT,
poolInfo: propPoolInfo,
configInfo: propConfigInfo,
platformFeeRate,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
sellAmount,
minAmountB: propMinAmountB,
slippage,
shareFeeRate = new BN(0),
shareFeeReceiver,
associatedOnly = true,
checkCreateATAOwner = false,
skipCheckMintA = false,
}: SellToken<T>): Promise<MakeTxData<T, { outAmount: BN }>> {
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
const txBuilder = this.createTxBuilder(feePayer);
if (sellAmount.lte(new BN(0))) this.logAndCreateError("sell amount should be gt 0");
const { publicKey: poolId } = getPdaLaunchpadPoolId(programId, mintA, mintB);
let transferFeeConfigA: TransferFeeConfig | undefined;
if (!skipCheckMintA) {
const mintInfo = await this.scope.connection.getAccountInfo(mintA);
if (mintInfo && mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
mintAProgram = mintInfo.owner;
const onlineData = unpackMint(mintA, mintInfo, mintAProgram);
transferFeeConfigA = getTransferFeeConfig(onlineData) || undefined;
}
}
let userTokenAccountA: PublicKey | null = null;
let userTokenAccountB: PublicKey | null = null;
const mintBUseSOLBalance = mintB.equals(NATIVE_MINT);
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: mintAProgram,
mint: mintA,
owner: this.scope.ownerPubKey,
createInfo: undefined,
skipCloseAccount: true,
notUseTokenAccount: false,
associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) userTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
if (userTokenAccountA === undefined)
this.logAndCreateError("cannot found mintA token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
mint: mintB,
owner: this.scope.ownerPubKey,
createInfo: mintBUseSOLBalance
? {
payer: this.scope.ownerPubKey!,
amount: 0,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) userTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (userTokenAccountB === undefined)
this.logAndCreateError("cannot found mintB token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
let poolInfo = propPoolInfo;
if (!poolInfo) {
const poolData = await this.scope.connection.getAccountInfo(poolId, { commitment: "processed" });
if (!poolData) this.logAndCreateError("cannot found pool", poolId.toBase58());
poolInfo = LaunchpadPool.decode(poolData!.data);
}
let configInfo = propConfigInfo;
const allData = await getMultipleAccountsInfoWithCustomFlags(
this.scope.connection,
[configInfo ? undefined : poolInfo.configId, poolInfo.platformId]
.filter(Boolean)
.map((key) => ({ pubkey: key! })),
);
if (!configInfo) {
const data = allData.find((d) => d.pubkey.equals(poolInfo!.configId));
if (!data || !data.accountInfo) this.logAndCreateError("config not found: ", poolInfo.configId.toBase58());
configInfo = LaunchpadConfig.decode(data!.accountInfo!.data);
}
const platformData = allData.find((d) => d.pubkey.equals(poolInfo!.platformId));
if (!platformData || !platformData.accountInfo)
this.logAndCreateError("platform info not found: ", poolInfo.configId.toBase58());
const platformInfo = PlatformConfig.decode(platformData!.accountInfo!.data);
platformFeeRate = platformFeeRate || platformInfo.feeRate;
const calculatedAmount = Curve.sellExactIn({
poolInfo,
amountA: sellAmount,
protocolFeeRate: configInfo.tradeFeeRate,
platformFeeRate,
curveType: configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA,
slot: await this.scope.connection.getSlot(),
});
const decimalAmountB = new Decimal(calculatedAmount.amountB.toString());
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.sub(slippage).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(0, 1)
: new Decimal(1);
const minAmountB =
propMinAmountB ?? (slippage ? new BN(decimalAmountB.mul(multiplier).toFixed(0)) : calculatedAmount.amountB);
if (minAmountB.lte(new BN(0))) this.logAndCreateError(`out ${mintB.toBase58()} amount should be gt 0`);
const shareATA = shareFeeReceiver ? getATAAddress(shareFeeReceiver, mintB, TOKEN_PROGRAM_ID).publicKey : undefined;
if (shareATA) {
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(this.scope.ownerPubKey, shareATA, shareFeeReceiver!, mintB),
],
});
}
txBuilder.addInstruction({
instructions: [
sellExactInInstruction(
programId,
this.scope.ownerPubKey,
authProgramId,
poolInfo.configId,
poolInfo.platformId,
poolId,
userTokenAccountA!,
userTokenAccountB!,
poolInfo.vaultA,
poolInfo.vaultB,
mintA,
mintB,
mintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(programId, poolInfo.platformId, mintB).publicKey,
getPdaCreatorVault(programId, poolInfo.creator, mintB).publicKey,
calculatedAmount.amountA.amount.lt(sellAmount) ? calculatedAmount.amountA.amount : sellAmount,
minAmountB,
shareFeeRate,
shareATA,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ outAmount: BN }>({
txVersion,
extInfo: {
outAmount: minAmountB,
},
}) as Promise<MakeTxData<T, { outAmount: BN }>>;
}
public async sellTokenExactOut<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
mintAProgram = TOKEN_PROGRAM_ID,
mintA,
mintB = NATIVE_MINT,
poolInfo: propPoolInfo,
configInfo: propConfigInfo,
platformFeeRate,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
inAmount,
maxSellAmount,
slippage,
shareFeeRate = new BN(0),
shareFeeReceiver,
associatedOnly = true,
checkCreateATAOwner = false,
skipCheckMintA = false,
}: SellTokenExactOut<T>): Promise<MakeTxData<T, { maxSellAmount: BN }>> {
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
const txBuilder = this.createTxBuilder(feePayer);
if (maxSellAmount?.lte(new BN(0))) this.logAndCreateError("max sell amount should be gt 0");
const { publicKey: poolId } = getPdaLaunchpadPoolId(programId, mintA, mintB);
let transferFeeConfigA: TransferFeeConfig | undefined;
if (!skipCheckMintA) {
const mintInfo = await this.scope.connection.getAccountInfo(mintA);
if (mintInfo && mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID)) {
mintAProgram = mintInfo.owner;
const onlineData = unpackMint(mintA, mintInfo, mintAProgram);
transferFeeConfigA = getTransferFeeConfig(onlineData) || undefined;
}
}
let userTokenAccountA: PublicKey | null = null;
let userTokenAccountB: PublicKey | null = null;
const mintBUseSOLBalance = mintB.equals(NATIVE_MINT);
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: mintAProgram,
mint: mintA,
owner: this.scope.ownerPubKey,
createInfo: undefined,
skipCloseAccount: true,
notUseTokenAccount: false,
associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) userTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
if (userTokenAccountA === undefined)
this.logAndCreateError("cannot found mintA token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
mint: mintB,
owner: this.scope.ownerPubKey,
createInfo: mintBUseSOLBalance
? {
payer: this.scope.ownerPubKey!,
amount: 0,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) userTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (userTokenAccountB === undefined)
this.logAndCreateError("cannot found mintB token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
let poolInfo = propPoolInfo;
if (!poolInfo) {
const poolData = await this.scope.connection.getAccountInfo(poolId, { commitment: "processed" });
if (!poolData) this.logAndCreateError("cannot found pool", poolId.toBase58());
poolInfo = LaunchpadPool.decode(poolData!.data);
}
let configInfo = propConfigInfo;
const allData = await getMultipleAccountsInfoWithCustomFlags(
this.scope.connection,
[configInfo ? undefined : poolInfo.configId, poolInfo.platformId]
.filter(Boolean)
.map((key) => ({ pubkey: key! })),
);
if (!configInfo) {
const data = allData.find((d) => d.pubkey.equals(poolInfo!.configId));
if (!data || !data.accountInfo) this.logAndCreateError("config not found: ", poolInfo.configId.toBase58());
configInfo = LaunchpadConfig.decode(data!.accountInfo!.data);
}
const platformData = allData.find((d) => d.pubkey.equals(poolInfo!.platformId));
if (!platformData || !platformData.accountInfo)
this.logAndCreateError("platform info not found: ", poolInfo.configId.toBase58());
const platformInfo = PlatformConfig.decode(platformData!.accountInfo!.data);
platformFeeRate = platformFeeRate || platformInfo.feeRate;
const calculatedAmount = Curve.sellExactOut({
poolInfo,
amountB: inAmount,
protocolFeeRate: configInfo.tradeFeeRate,
platformFeeRate,
curveType: configInfo.curveType,
shareFeeRate,
creatorFeeRate: platformInfo.creatorFeeRate,
transferFeeConfigA,
slot: await this.scope.connection.getSlot(),
});
const decimalAmountA = new Decimal(calculatedAmount.amountA.amount.toString());
const multiplier = slippage
? new Decimal(SLIPPAGE_UNIT.add(slippage).toNumber() / SLIPPAGE_UNIT.toNumber()).clampedTo(
0,
Number.MAX_SAFE_INTEGER,
)
: new Decimal(1);
const maxSellAmountA =
maxSellAmount ?? slippage ? new BN(decimalAmountA.mul(multiplier).toFixed(0)) : calculatedAmount.amountA.amount;
const shareATA = shareFeeReceiver ? getATAAddress(shareFeeReceiver, mintB, TOKEN_PROGRAM_ID).publicKey : undefined;
if (shareATA) {
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(this.scope.ownerPubKey, shareATA, shareFeeReceiver!, mintB),
],
});
}
txBuilder.addInstruction({
instructions: [
sellExactOut(
programId,
this.scope.ownerPubKey,
authProgramId,
poolInfo.configId,
poolInfo.platformId,
poolId,
userTokenAccountA!,
userTokenAccountB!,
poolInfo.vaultA,
poolInfo.vaultB,
mintA,
mintB,
mintAProgram,
TOKEN_PROGRAM_ID,
getPdaPlatformVault(programId, poolInfo.platformId, mintB).publicKey,
getPdaCreatorVault(programId, poolInfo.creator, mintB).publicKey,
inAmount,
maxSellAmountA,
shareFeeRate,
shareATA,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ maxSellAmount: BN }>({
txVersion,
extInfo: {
maxSellAmount: maxSellAmountA,
},
}) as Promise<MakeTxData<T, { maxSellAmount: BN }>>;
}
public async createPlatformConfig<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
platformAdmin,
platformClaimFeeWallet,
platformLockNftWallet,
platformVestingWallet,
cpConfigId,
migrateCpLockNftScale,
transferFeeExtensionAuth,
creatorFeeRate,
feeRate,
name,
web,
img,
platformVestingScale = new BN(0), // max: 1_000_000 = 100%
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: CreatePlatform<T>): Promise<MakeTxData<T, { platformId: PublicKey }>> {
const txBuilder = this.createTxBuilder(feePayer);
const { publicKey: platformId } = getPdaPlatformId(programId, platformAdmin);
txBuilder.addInstruction({
instructions: [
createPlatformConfig(
programId,
platformAdmin,
platformClaimFeeWallet,
platformLockNftWallet,
platformVestingWallet,
platformId,
cpConfigId,
transferFeeExtensionAuth,
migrateCpLockNftScale,
feeRate,
creatorFeeRate,
name,
web,
img,
platformVestingScale,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
extInfo: {
platformId,
},
}) as Promise<MakeTxData<T, { platformId: PublicKey }>>;
}
public async updatePlatformConfig<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
platformAdmin,
platformId: propsPlatformId,
updateInfo,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: UpdatePlatform<T>): Promise<MakeTxData> {
const txBuilder = this.createTxBuilder(feePayer);
const platformId = propsPlatformId ?? getPdaPlatformId(programId, platformAdmin).publicKey;
txBuilder.addInstruction({
instructions: [updatePlatformConfig(programId, platformAdmin, platformId, updateInfo)],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
}) as Promise<MakeTxData>;
}
public async createPlatformVestingAccount<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
platformVestingWallet,
beneficiary,
platformId,
poolId,
vestingRecord: propsVestingRecord,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: CreatePlatformVestingAccount<T>): Promise<MakeTxData<T>> {
const txBuilder = this.createTxBuilder(feePayer);
const vestingRecord = propsVestingRecord ?? getPdaVestId(programId, poolId, beneficiary).publicKey;
txBuilder.addInstruction({
instructions: [
createPlatformVestingAccountIns(
programId,
platformVestingWallet,
beneficiary,
platformId,
poolId,
vestingRecord,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
}) as Promise<MakeTxData<T>>;
}
public async claimPlatformFee<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
platformId,
poolId,
platformClaimFeeWallet,
mintB: propsMintB,
vaultB: propsVaultB,
mintBProgram = TOKEN_PROGRAM_ID,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: ClaimPlatformFee<T>): Promise<MakeTxData> {
const txBuilder = this.createTxBuilder(feePayer);
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
let mintB = propsMintB;
let vaultB = propsVaultB;
if (!mintB) {
const poolData = await this.scope.connection.getAccountInfo(poolId, { commitment: "processed" });
if (!poolData) this.logAndCreateError("cannot found pool:", poolId.toBase58());
const poolInfo = LaunchpadPool.decode(poolData!.data);
const configData = await this.scope.connection.getAccountInfo(poolInfo.configId, { commitment: "processed" });
if (!configData) this.logAndCreateError("cannot found config:", poolInfo.configId.toBase58());
const configInfo = LaunchpadConfig.decode(configData!.data);
mintB = configInfo.mintB;
vaultB = vaultB ?? poolInfo.vaultB;
}
if (!mintB || !vaultB) {
this.logAndCreateError(
"cannot found mint info, mintB: ",
mintB.toBase58(),
", vaultB: ",
vaultB?.toBase58() ?? "",
);
}
const userTokenAccountB = getATAAddress(this.scope.ownerPubKey, mintB, TOKEN_PROGRAM_ID).publicKey;
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
userTokenAccountB,
this.scope.ownerPubKey,
mintB,
),
],
});
txBuilder.addInstruction({
instructions: [
claimPlatformFee(
programId,
platformClaimFeeWallet,
authProgramId,
poolId,
platformId,
vaultB!,
userTokenAccountB!,
mintB,
mintBProgram,
),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
}) as Promise<MakeTxData>;
}
public async claimAllPlatformFee<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
authProgramId,
platformId,
platformClaimFeeWallet,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: ClaimAllPlatformFee<T>): Promise<MakeMultiTxData<T>> {
const txBuilder = this.createTxBuilder(feePayer);
authProgramId = authProgramId ?? getPdaLaunchpadAuth(programId).publicKey;
const allPlatformPool = await this.scope.connection.getProgramAccounts(programId, {
filters: [
{ dataSize: LaunchpadPool.span },
{ memcmp: { offset: LaunchpadPool.offsetOf("platformId"), bytes: platformId.toString() } },
],
});
allPlatformPool.forEach((data) => {
const pool = LaunchpadPool.decode(data.account.data);
if (pool.platformFee.lte(new BN(0))) return;
const userTokenAccountB = getATAAddress(this.scope.ownerPubKey, pool.mintB, TOKEN_PROGRAM_ID).publicKey;
txBuilder.addInstruction({
instructions: [
createAssociatedTokenAccountIdempotentInstruction(
this.scope.ownerPubKey,
userTokenAccountB,
this.scope.ownerPubKey,
pool.mintB,
),
],
});
txBuilder.addInstruction({
instructions: [
claimPlatformFee(
programId,
platformClaimFeeWallet,
authProgramId!,
data.pubkey,
platformId,
pool.vaultB,
userTokenAccountB!,
pool.mintB,
TOKEN_PROGRAM_ID,
),
],
});
});
txBuilder.addTipInstruction(txTipConfig);
if (txVersion === TxVersion.V0)
return txBuilder.sizeCheckBuildV0({ computeBudgetConfig }) as Promise<MakeMultiTxData<T>>;
return txBuilder.sizeCheckBuild({
computeBudgetConfig,
}) as Promise<MakeMultiTxData<T>>;
}
public async createVesting<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
poolId,
beneficiary,
shareAmount,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: CreateVesting<T>): Promise<MakeTxData> {
const txBuilder = this.createTxBuilder(feePayer);
const poolInfo = await this.getRpcPoolInfo({ poolId });
if (shareAmount.add(poolInfo.vestingSchedule.totalAllocatedShare).gt(poolInfo.vestingSchedule.totalLockedAmount))
this.logAndCreateError("share amount exceed total locked amount");
const vestingRecord = getPdaVestId(programId, poolId, beneficiary).publicKey;
txBuilder.addInstruction({
instructions: [
createVestingAccount(programId, this.scope.ownerPubKey, beneficiary, poolId, vestingRecord, shareAmount),
],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
}) as Promise<MakeTxData>;
}
public async createMultipleVesting<T extends TxVersion>({
programId = LAUNCHPAD_PROGRAM,
poolId,
beneficiaryList,
txVersion,
computeBudgetConfig,
feePayer,
}: CreateMultipleVesting<T>): Promise<MakeMultiTxData<T>> {
const txBuilder = this.createTxBuilder(feePayer);
if (beneficiaryList.length === 0) this.logAndCreateError("beneficiaryList is null");
const poolInfo = await this.getRpcPoolInfo({ pool