@raydium-io/raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
1,317 lines (1,226 loc) • 80.6 kB
text/typescript
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import Decimal from "decimal.js";
import { ApiV3PoolInfoConcentratedItem, ClmmKeys } from "../../api/type";
import {
CLMM_LOCK_AUTH_ID,
CLMM_LOCK_PROGRAM_ID,
CLMM_PROGRAM_ID,
InstructionType,
WSOLMint,
fetchMultipleMintInfos,
getATAAddress,
getMultipleAccountsInfoWithCustomFlags,
} from "@/common";
import {
AccountLayout,
createAssociatedTokenAccountIdempotentInstruction,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { MakeMultiTxData, MakeTxData } from "@/common/txTool/txTool";
import { TxVersion } from "@/common/txTool/txType";
import { toApiV3Token, toFeeConfig } from "../../raydium/token/utils";
import { ComputeBudgetConfig, ReturnTypeFetchMultipleMintInfos, TxTipConfig } from "../../raydium/type";
import ModuleBase, { ModuleBaseProps } from "../moduleBase";
import { MakeTransaction } from "../type";
import { ClmmInstrument } from "./instrument";
import {
ClmmConfigLayout,
ClmmPositionLayout,
LockClPositionLayoutV2,
OperationLayout,
PoolInfoLayout,
PositionInfoLayout,
} from "./layout";
import {
ClmmParsedRpcData,
ClosePositionExtInfo,
CollectRewardParams,
CollectRewardsParams,
ComputeClmmPoolInfo,
CreateConcentratedPool,
DecreaseLiquidity,
HarvestAllRewardsParams,
HarvestLockPosition,
IncreasePositionFromBase,
IncreasePositionFromLiquidity,
InitRewardExtInfo,
InitRewardParams,
InitRewardsParams,
LockPosition,
ManipulateLiquidityExtInfo,
OpenPositionFromBase,
OpenPositionFromBaseExtInfo,
OpenPositionFromLiquidity,
OpenPositionFromLiquidityExtInfo,
ReturnTypeFetchMultiplePoolTickArrays,
SetRewardParams,
SetRewardsParams,
ClmmLockAddress,
} from "./type";
import { MAX_SQRT_PRICE_X64, MIN_SQRT_PRICE_X64, mockV3CreatePoolInfo, ZERO } from "./utils/constants";
import { MathUtil, SqrtPriceMath } from "./utils/math";
import {
getPdaOperationAccount,
getPdaPersonalPositionAddress,
getPdaLockClPositionIdV2,
getPdaTickArrayAddress,
getPdaProtocolPositionAddress,
getPdaExBitmapAccount,
getPdaMintExAccount,
} from "./utils/pda";
import { PoolUtils, clmmComputeInfoToApiInfo } from "./utils/pool";
import { TickUtils } from "./utils/tick";
export class Clmm extends ModuleBase {
constructor(params: ModuleBaseProps) {
super(params);
}
public async getClmmPoolKeys(poolId: string): Promise<ClmmKeys> {
return ((await this.scope.api.fetchPoolKeysById({ idList: [poolId] })) as ClmmKeys[])[0];
}
public async createPool<T extends TxVersion>(
props: CreateConcentratedPool<T>,
): Promise<MakeTxData<T, { mockPoolInfo: ApiV3PoolInfoConcentratedItem; address: ClmmKeys }>> {
const {
programId,
owner = this.scope.owner?.publicKey || PublicKey.default,
mint1,
mint2,
ammConfig,
initialPrice,
computeBudgetConfig,
forerunCreate,
getObserveState,
txVersion,
txTipConfig,
feePayer,
} = props;
const txBuilder = this.createTxBuilder(feePayer);
const [mintA, mintB, initPrice] = new BN(new PublicKey(mint1.address).toBuffer()).gt(
new BN(new PublicKey(mint2.address).toBuffer()),
)
? [mint2, mint1, new Decimal(1).div(initialPrice)]
: [mint1, mint2, initialPrice];
const initialPriceX64 = SqrtPriceMath.priceToSqrtPriceX64(initPrice, mintA.decimals, mintB.decimals);
const extendMintAccount: PublicKey[] = [];
const fetchAccounts: PublicKey[] = [];
if (mintA.programId === TOKEN_2022_PROGRAM_ID.toBase58())
fetchAccounts.push(getPdaMintExAccount(programId, new PublicKey(mintA.address)).publicKey);
if (mintB.programId === TOKEN_2022_PROGRAM_ID.toBase58())
fetchAccounts.push(getPdaMintExAccount(programId, new PublicKey(mintB.address)).publicKey);
const extMintRes = await this.scope.connection.getMultipleAccountsInfo(fetchAccounts);
extMintRes.forEach((r, idx) => {
if (r) extendMintAccount.push(fetchAccounts[idx]);
});
const insInfo = await ClmmInstrument.createPoolInstructions({
connection: this.scope.connection,
programId,
owner,
mintA,
mintB,
ammConfigId: ammConfig.id,
initialPriceX64,
forerunCreate: !getObserveState && forerunCreate,
extendMintAccount,
});
txBuilder.addInstruction(insInfo);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{
mockPoolInfo: ApiV3PoolInfoConcentratedItem;
address: ClmmKeys;
forerunCreate?: boolean;
}>({
txVersion,
extInfo: {
address: {
...insInfo.address,
observationId: insInfo.address.observationId.toBase58(),
exBitmapAccount: insInfo.address.exBitmapAccount.toBase58(),
programId: programId.toString(),
id: insInfo.address.poolId.toString(),
mintA,
mintB,
openTime: "0",
vault: { A: insInfo.address.mintAVault.toString(), B: insInfo.address.mintBVault.toString() },
rewardInfos: [],
config: {
id: ammConfig.id.toString(),
index: ammConfig.index,
protocolFeeRate: ammConfig.protocolFeeRate,
tradeFeeRate: ammConfig.tradeFeeRate,
tickSpacing: ammConfig.tickSpacing,
fundFeeRate: ammConfig.fundFeeRate,
description: ammConfig.description,
defaultRange: 0,
defaultRangePoint: [],
},
},
mockPoolInfo: {
type: "Concentrated",
rewardDefaultPoolInfos: "Clmm",
id: insInfo.address.poolId.toString(),
mintA,
mintB,
feeRate: ammConfig.tradeFeeRate,
openTime: "0",
programId: programId.toString(),
price: initPrice.toNumber(),
config: {
id: ammConfig.id.toString(),
index: ammConfig.index,
protocolFeeRate: ammConfig.protocolFeeRate,
tradeFeeRate: ammConfig.tradeFeeRate,
tickSpacing: ammConfig.tickSpacing,
fundFeeRate: ammConfig.fundFeeRate,
description: ammConfig.description,
defaultRange: 0,
defaultRangePoint: [],
},
burnPercent: 0,
...mockV3CreatePoolInfo,
},
forerunCreate,
},
}) as Promise<MakeTxData<T, { mockPoolInfo: ApiV3PoolInfoConcentratedItem; address: ClmmKeys }>>;
}
public async openPositionFromBase<T extends TxVersion>({
poolInfo,
poolKeys: propPoolKeys,
ownerInfo,
tickLower,
tickUpper,
base,
baseAmount,
otherAmountMax,
nft2022,
associatedOnly = true,
checkCreateATAOwner = false,
withMetadata = "create",
getEphemeralSigners,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
}: OpenPositionFromBase<T>): Promise<MakeTxData<T, OpenPositionFromBaseExtInfo>> {
if (this.scope.availability.addConcentratedPosition === false)
this.logAndCreateError("add position feature disabled in your region");
this.scope.checkOwner();
const txBuilder = this.createTxBuilder(feePayer);
let ownerTokenAccountA: PublicKey | null = null;
let ownerTokenAccountB: PublicKey | null = null;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintA.address === WSOLMint.toString();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintB.address === WSOLMint.toString();
const [amountA, amountB] = base === "MintA" ? [baseAmount, otherAmountMax] : [otherAmountMax, baseAmount];
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintA.programId,
mint: new PublicKey(poolInfo.mintA.address),
owner: this.scope.ownerPubKey,
createInfo:
mintAUseSOLBalance || amountA.isZero()
? {
payer: this.scope.ownerPubKey,
amount: amountA,
}
: undefined,
skipCloseAccount: !mintAUseSOLBalance,
notUseTokenAccount: mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) ownerTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintB.programId,
mint: new PublicKey(poolInfo.mintB.address),
owner: this.scope.ownerPubKey,
createInfo:
mintBUseSOLBalance || amountB.isZero()
? {
payer: this.scope.ownerPubKey!,
amount: amountB,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) ownerTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (!ownerTokenAccountA || !ownerTokenAccountB)
this.logAndCreateError("cannot found target token accounts", "tokenAccounts", {
ownerTokenAccountA: ownerTokenAccountA?.toBase58(),
ownerTokenAccountB: ownerTokenAccountB?.toBase58(),
});
const poolKeys = propPoolKeys || (await this.getClmmPoolKeys(poolInfo.id));
const insInfo = await ClmmInstrument.openPositionFromBaseInstructions({
poolInfo,
poolKeys,
ownerInfo: {
...ownerInfo,
feePayer: this.scope.ownerPubKey,
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
tickLower,
tickUpper,
base,
baseAmount,
otherAmountMax,
withMetadata,
getEphemeralSigners,
nft2022,
});
txBuilder.addInstruction(insInfo);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<OpenPositionFromBaseExtInfo>({
txVersion,
extInfo: { ...insInfo.address },
}) as Promise<MakeTxData<T, OpenPositionFromBaseExtInfo>>;
}
public async openPositionFromLiquidity<T extends TxVersion>({
poolInfo,
poolKeys: propPoolKeys,
ownerInfo,
amountMaxA,
amountMaxB,
tickLower,
tickUpper,
liquidity,
associatedOnly = true,
checkCreateATAOwner = false,
withMetadata = "create",
txVersion,
computeBudgetConfig,
txTipConfig,
getEphemeralSigners,
nft2022,
feePayer,
}: OpenPositionFromLiquidity<T>): Promise<MakeTxData<T, OpenPositionFromLiquidityExtInfo>> {
if (this.scope.availability.createConcentratedPosition === false)
this.logAndCreateError("open position feature disabled in your region");
const txBuilder = this.createTxBuilder(feePayer);
let ownerTokenAccountA: PublicKey | null = null;
let ownerTokenAccountB: PublicKey | null = null;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintA.address === WSOLMint.toBase58();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintB.address === WSOLMint.toBase58();
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintA.programId,
mint: new PublicKey(poolInfo.mintA.address),
owner: this.scope.ownerPubKey,
createInfo:
mintAUseSOLBalance || amountMaxA.isZero()
? {
payer: this.scope.ownerPubKey,
amount: amountMaxA,
}
: undefined,
skipCloseAccount: !mintAUseSOLBalance,
notUseTokenAccount: mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) ownerTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintB.programId,
mint: new PublicKey(poolInfo.mintB.address),
owner: this.scope.ownerPubKey,
createInfo:
mintBUseSOLBalance || amountMaxB.isZero()
? {
payer: this.scope.ownerPubKey!,
amount: amountMaxB,
}
: undefined,
skipCloseAccount: !mintBUseSOLBalance,
notUseTokenAccount: mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) ownerTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (ownerTokenAccountA === undefined || ownerTokenAccountB === undefined)
this.logAndCreateError("cannot found target token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
const poolKeys = propPoolKeys || (await this.getClmmPoolKeys(poolInfo.id));
const makeOpenPositionInstructions = await ClmmInstrument.openPositionFromLiquidityInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
tickLower,
tickUpper,
liquidity,
amountMaxA,
amountMaxB,
withMetadata,
getEphemeralSigners,
nft2022,
});
txBuilder.addInstruction(makeOpenPositionInstructions);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<OpenPositionFromLiquidityExtInfo>({
txVersion,
extInfo: { address: makeOpenPositionInstructions.address },
}) as Promise<MakeTxData<T, OpenPositionFromLiquidityExtInfo>>;
}
public async increasePositionFromLiquidity<T extends TxVersion>(
props: IncreasePositionFromLiquidity<T>,
): Promise<MakeTxData<T, ManipulateLiquidityExtInfo>> {
const {
poolInfo,
poolKeys: propPoolKeys,
ownerPosition,
amountMaxA,
amountMaxB,
liquidity,
ownerInfo,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
} = props;
const txBuilder = this.createTxBuilder(feePayer);
let ownerTokenAccountA: PublicKey | undefined = undefined;
let ownerTokenAccountB: PublicKey | undefined = undefined;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintA.address === WSOLMint.toString();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintB.address === WSOLMint.toString();
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintA.programId,
mint: new PublicKey(poolInfo.mintA.address),
notUseTokenAccount: mintAUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo:
mintAUseSOLBalance || amountMaxA.isZero()
? {
payer: this.scope.ownerPubKey,
amount: amountMaxA,
}
: undefined,
skipCloseAccount: !mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) ownerTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintB.programId,
mint: new PublicKey(poolInfo.mintB.address),
owner: this.scope.ownerPubKey,
createInfo:
mintBUseSOLBalance || amountMaxB.isZero()
? {
payer: this.scope.ownerPubKey!,
amount: amountMaxB,
}
: undefined,
notUseTokenAccount: mintBUseSOLBalance,
skipCloseAccount: !mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) ownerTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (!ownerTokenAccountA && !ownerTokenAccountB)
this.logAndCreateError("cannot found target token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
const poolKeys = propPoolKeys ?? (await this.getClmmPoolKeys(poolInfo.id));
const ins = ClmmInstrument.increasePositionFromLiquidityInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
liquidity,
amountMaxA,
amountMaxB,
nft2022: (await this.scope.connection.getAccountInfo(ownerPosition.nftMint))?.owner.equals(TOKEN_2022_PROGRAM_ID),
});
txBuilder.addInstruction(ins);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<ManipulateLiquidityExtInfo>({
txVersion,
extInfo: { address: ins.address },
}) as Promise<MakeTxData<T, ManipulateLiquidityExtInfo>>;
}
public async increasePositionFromBase<T extends TxVersion>(
props: IncreasePositionFromBase<T>,
): Promise<MakeTxData<T, ManipulateLiquidityExtInfo>> {
const {
poolInfo,
ownerPosition,
base,
baseAmount,
otherAmountMax,
ownerInfo,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
} = props;
const txBuilder = this.createTxBuilder(feePayer);
let ownerTokenAccountA: PublicKey | undefined = undefined;
let ownerTokenAccountB: PublicKey | undefined = undefined;
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintA.address === WSOLMint.toString();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintB.address === WSOLMint.toString();
const { account: _ownerTokenAccountA, instructionParams: _tokenAccountAInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintA.programId,
mint: new PublicKey(poolInfo.mintA.address),
notUseTokenAccount: mintAUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo:
mintAUseSOLBalance || (base === "MintA" ? baseAmount : otherAmountMax).isZero()
? {
payer: this.scope.ownerPubKey,
amount: base === "MintA" ? baseAmount : otherAmountMax,
}
: undefined,
skipCloseAccount: !mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountA) ownerTokenAccountA = _ownerTokenAccountA;
txBuilder.addInstruction(_tokenAccountAInstruction || {});
const { account: _ownerTokenAccountB, instructionParams: _tokenAccountBInstruction } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintB.programId,
mint: new PublicKey(poolInfo.mintB.address),
owner: this.scope.ownerPubKey,
createInfo:
mintBUseSOLBalance || (base === "MintA" ? otherAmountMax : baseAmount).isZero()
? {
payer: this.scope.ownerPubKey!,
amount: base === "MintA" ? otherAmountMax : baseAmount,
}
: undefined,
notUseTokenAccount: mintBUseSOLBalance,
skipCloseAccount: !mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
if (_ownerTokenAccountB) ownerTokenAccountB = _ownerTokenAccountB;
txBuilder.addInstruction(_tokenAccountBInstruction || {});
if (!ownerTokenAccountA && !ownerTokenAccountB)
this.logAndCreateError("cannot found target token accounts", "tokenAccounts", this.scope.account.tokenAccounts);
const poolKeys = await this.getClmmPoolKeys(poolInfo.id);
const ins = ClmmInstrument.increasePositionFromBaseInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
},
base,
baseAmount,
otherAmountMax,
nft2022: (await this.scope.connection.getAccountInfo(ownerPosition.nftMint))?.owner.equals(TOKEN_2022_PROGRAM_ID),
});
txBuilder.addInstruction(ins);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<ManipulateLiquidityExtInfo>({
txVersion,
extInfo: { address: ins.address },
}) as Promise<MakeTxData<T, ManipulateLiquidityExtInfo>>;
}
public async decreaseLiquidity<T extends TxVersion>(
props: DecreaseLiquidity<T>,
): Promise<MakeTxData<T, ManipulateLiquidityExtInfo & Partial<ClosePositionExtInfo>>> {
const {
poolInfo,
poolKeys: propPoolKeys,
ownerPosition,
ownerInfo,
amountMinA,
amountMinB,
liquidity,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
} = props;
if (this.scope.availability.removeConcentratedPosition === false)
this.logAndCreateError("remove position feature disabled in your region");
const txBuilder = this.createTxBuilder(feePayer);
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintA.address === WSOLMint.toString();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolInfo.mintB.address === WSOLMint.toString();
let ownerTokenAccountA: PublicKey | undefined = undefined;
let ownerTokenAccountB: PublicKey | undefined = undefined;
const { account: _ownerTokenAccountA, instructionParams: accountAInstructions } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintA.programId,
mint: new PublicKey(poolInfo.mintA.address),
notUseTokenAccount: mintAUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
skipCloseAccount: !mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerTokenAccountA = _ownerTokenAccountA;
accountAInstructions && txBuilder.addInstruction(accountAInstructions);
const { account: _ownerTokenAccountB, instructionParams: accountBInstructions } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolInfo.mintB.programId,
mint: new PublicKey(poolInfo.mintB.address),
notUseTokenAccount: mintBUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
skipCloseAccount: !mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerTokenAccountB = _ownerTokenAccountB;
accountBInstructions && txBuilder.addInstruction(accountBInstructions);
const rewardAccounts: PublicKey[] = [];
for (const itemReward of poolInfo.rewardDefaultInfos) {
const rewardUseSOLBalance = ownerInfo.useSOLBalance && itemReward.mint.address === WSOLMint.toString();
let ownerRewardAccount: PublicKey | undefined;
if (itemReward.mint.address === poolInfo.mintA.address) ownerRewardAccount = ownerTokenAccountA;
else if (itemReward.mint.address === poolInfo.mintB.address) ownerRewardAccount = ownerTokenAccountB;
else {
const { account: _ownerRewardAccount, instructionParams: ownerRewardAccountInstructions } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(itemReward.mint.programId),
mint: new PublicKey(itemReward.mint.address),
notUseTokenAccount: rewardUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
skipCloseAccount: !rewardUseSOLBalance,
associatedOnly: rewardUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardAccount = _ownerRewardAccount;
ownerRewardAccountInstructions && txBuilder.addInstruction(ownerRewardAccountInstructions);
}
rewardAccounts.push(ownerRewardAccount!);
}
if (!ownerTokenAccountA && !ownerTokenAccountB)
this.logAndCreateError(
"cannot found target token accounts",
"tokenAccounts",
this.scope.account.tokenAccountRawInfos,
);
const poolKeys = propPoolKeys ?? (await this.getClmmPoolKeys(poolInfo.id));
const nft2022 = (await this.scope.connection.getAccountInfo(ownerPosition.nftMint))?.owner.equals(
TOKEN_2022_PROGRAM_ID,
);
const decreaseInsInfo = await ClmmInstrument.decreaseLiquidityInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccountA: ownerTokenAccountA!,
tokenAccountB: ownerTokenAccountB!,
rewardAccounts,
},
liquidity,
amountMinA,
amountMinB,
nft2022,
});
txBuilder.addInstruction({
instructions: decreaseInsInfo.instructions,
instructionTypes: [InstructionType.ClmmDecreasePosition],
});
let extInfo = { ...decreaseInsInfo.address };
if (ownerInfo.closePosition) {
const closeInsInfo = await ClmmInstrument.closePositionInstructions({
poolInfo,
poolKeys,
ownerInfo: { wallet: this.scope.ownerPubKey },
ownerPosition,
nft2022,
});
txBuilder.addInstruction({
endInstructions: closeInsInfo.instructions,
endInstructionTypes: closeInsInfo.instructionTypes,
});
extInfo = { ...extInfo, ...closeInsInfo.address };
}
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<ManipulateLiquidityExtInfo>({
txVersion,
extInfo: { address: extInfo },
}) as Promise<MakeTxData<T, ManipulateLiquidityExtInfo>>;
}
public async lockPosition<T extends TxVersion>(props: LockPosition<T>): Promise<MakeTxData<ClmmLockAddress>> {
const {
programId = CLMM_LOCK_PROGRAM_ID,
authProgramId = CLMM_LOCK_AUTH_ID,
poolProgramId = CLMM_PROGRAM_ID,
ownerPosition,
payer,
computeBudgetConfig,
txTipConfig,
txVersion,
getEphemeralSigners,
feePayer,
} = props;
const txBuilder = this.createTxBuilder(feePayer);
const lockIns = await ClmmInstrument.makeLockPositions({
programId,
authProgramId,
poolProgramId,
wallet: this.scope.ownerPubKey,
payer: payer ?? this.scope.ownerPubKey,
nftMint: ownerPosition.nftMint,
getEphemeralSigners,
nft2022: (await this.scope.connection.getAccountInfo(ownerPosition.nftMint))?.owner.equals(TOKEN_2022_PROGRAM_ID),
});
txBuilder.addInstruction(lockIns);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
extInfo: lockIns.address,
}) as Promise<MakeTxData<ClmmLockAddress>>;
}
public async harvestLockPosition<T extends TxVersion>(props: HarvestLockPosition<T>): Promise<MakeTxData<T>> {
const {
programId = CLMM_LOCK_PROGRAM_ID,
authProgramId = CLMM_LOCK_AUTH_ID,
clmmProgram = CLMM_PROGRAM_ID,
poolKeys: propPoolKeys,
lockData,
ownerInfo = { useSOLBalance: true },
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
} = props;
const poolKeys = propPoolKeys || (await this.getClmmPoolKeys(lockData.poolId.toString()));
const txBuilder = this.createTxBuilder(feePayer);
const positionData = await this.scope.connection.getAccountInfo(lockData.positionId);
if (!positionData) this.logger.logWithError("position not found", lockData.positionId);
const position = PositionInfoLayout.decode(positionData!.data);
const mintAUseSOLBalance = ownerInfo.useSOLBalance && poolKeys.mintA.address === WSOLMint.toString();
const mintBUseSOLBalance = ownerInfo.useSOLBalance && poolKeys.mintB.address === WSOLMint.toString();
let ownerTokenAccountA: PublicKey | undefined = undefined;
let ownerTokenAccountB: PublicKey | undefined = undefined;
const { account: _ownerTokenAccountA, instructionParams: accountAInstructions } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolKeys.mintA.programId,
mint: new PublicKey(poolKeys.mintA.address),
notUseTokenAccount: mintAUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
skipCloseAccount: !mintAUseSOLBalance,
associatedOnly: mintAUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerTokenAccountA = _ownerTokenAccountA;
accountAInstructions && txBuilder.addInstruction(accountAInstructions);
const { account: _ownerTokenAccountB, instructionParams: accountBInstructions } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: poolKeys.mintB.programId,
mint: new PublicKey(poolKeys.mintB.address),
notUseTokenAccount: mintBUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
skipCloseAccount: !mintBUseSOLBalance,
associatedOnly: mintBUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerTokenAccountB = _ownerTokenAccountB;
accountBInstructions && txBuilder.addInstruction(accountBInstructions);
const ownerMintToAccount: { [mint: string]: PublicKey } = {};
const rewardAccounts: PublicKey[] = [];
for (const itemReward of poolKeys.rewardInfos) {
const rewardUseSOLBalance = ownerInfo.useSOLBalance && itemReward.mint.address === WSOLMint.toString();
let ownerRewardAccount = ownerMintToAccount[itemReward.mint.address];
if (!ownerRewardAccount) {
const { account, instructionParams } = await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(itemReward.mint.programId),
mint: new PublicKey(itemReward.mint.address),
notUseTokenAccount: rewardUseSOLBalance,
owner: this.scope.ownerPubKey,
skipCloseAccount: !rewardUseSOLBalance,
createInfo: {
payer: this.scope.ownerPubKey,
amount: 0,
},
associatedOnly: rewardUseSOLBalance ? false : associatedOnly,
});
ownerRewardAccount = account!;
instructionParams && txBuilder.addInstruction(instructionParams);
}
ownerMintToAccount[itemReward.mint.address] = ownerRewardAccount;
rewardAccounts.push(ownerRewardAccount!);
}
const lockPositionId = getPdaLockClPositionIdV2(programId, lockData.lockNftMint).publicKey;
const lockNftAccount = getATAAddress(this.scope.ownerPubKey, lockData.lockNftMint, TOKEN_PROGRAM_ID).publicKey;
const tickArrayLowerStartIndex = TickUtils.getTickArrayStartIndexByTick(
position.tickLower,
poolKeys.config.tickSpacing,
);
const tickArrayUpperStartIndex = TickUtils.getTickArrayStartIndexByTick(
position.tickUpper,
poolKeys.config.tickSpacing,
);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(
new PublicKey(poolKeys.programId),
lockData.poolId,
tickArrayLowerStartIndex,
);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(
new PublicKey(poolKeys.programId),
lockData.poolId,
tickArrayUpperStartIndex,
);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
new PublicKey(poolKeys.programId),
lockData.poolId,
position.tickLower,
position.tickUpper,
);
const rewardAccountsFullInfo: {
poolRewardVault: PublicKey;
ownerRewardVault: PublicKey;
rewardMint: PublicKey;
}[] = [];
for (let i = 0; i < poolKeys.rewardInfos.length; i++) {
rewardAccountsFullInfo.push({
poolRewardVault: new PublicKey(poolKeys.rewardInfos[i].vault),
ownerRewardVault: rewardAccounts[i],
rewardMint: new PublicKey(poolKeys.rewardInfos[i].mint.address),
});
}
const harvestLockIns = await ClmmInstrument.harvestLockPositionInstructionV2({
programId,
auth: authProgramId,
lockPositionId,
clmmProgram,
lockOwner: this.scope.ownerPubKey,
lockNftMint: lockData.lockNftMint,
lockNftAccount,
positionNftAccount: lockData.nftAccount,
positionId: lockData.positionId,
poolId: lockData.poolId,
protocolPosition,
vaultA: new PublicKey(poolKeys.vault.A),
vaultB: new PublicKey(poolKeys.vault.B),
tickArrayLower,
tickArrayUpper,
userVaultA: ownerTokenAccountA!,
userVaultB: ownerTokenAccountB!,
mintA: new PublicKey(poolKeys.mintA.address),
mintB: new PublicKey(poolKeys.mintB.address),
rewardAccounts: rewardAccountsFullInfo,
exTickArrayBitmap: getPdaExBitmapAccount(clmmProgram, lockData.poolId).publicKey,
});
txBuilder.addInstruction({
instructions: [harvestLockIns],
instructionTypes: [InstructionType.ClmmHarvestLockPosition],
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
}) as Promise<MakeTxData<T>>;
}
public async closePosition<T extends TxVersion>({
poolInfo,
poolKeys: propPoolKeys,
ownerPosition,
txVersion,
computeBudgetConfig,
txTipConfig,
feePayer,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys?: ClmmKeys;
ownerPosition: ClmmPositionLayout;
computeBudgetConfig?: ComputeBudgetConfig;
txTipConfig?: TxTipConfig;
txVersion: T;
feePayer?: PublicKey;
}): Promise<MakeTxData<T, ClosePositionExtInfo>> {
if (this.scope.availability.removeConcentratedPosition === false)
this.logAndCreateError("remove position feature disabled in your region");
const txBuilder = this.createTxBuilder(feePayer);
const poolKeys = propPoolKeys ?? (await this.getClmmPoolKeys(poolInfo.id));
const ins = ClmmInstrument.closePositionInstructions({
poolInfo,
poolKeys,
ownerInfo: { wallet: this.scope.ownerPubKey },
ownerPosition,
nft2022: (await this.scope.connection.getAccountInfo(ownerPosition.nftMint))?.owner.equals(TOKEN_2022_PROGRAM_ID),
});
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.addInstruction(ins).versionBuild<ClosePositionExtInfo>({
txVersion,
extInfo: { address: ins.address },
}) as Promise<MakeTxData<T, ClosePositionExtInfo>>;
}
public async initReward<T extends TxVersion>({
poolInfo,
ownerInfo,
rewardInfo,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txVersion,
feePayer,
}: InitRewardParams<T>): Promise<MakeTxData<T, InitRewardExtInfo>> {
if (rewardInfo.endTime <= rewardInfo.openTime)
this.logAndCreateError("reward time error", "rewardInfo", rewardInfo);
const txBuilder = this.createTxBuilder(feePayer);
const rewardMintUseSOLBalance =
ownerInfo.useSOLBalance && rewardInfo.mint.address.toString() === WSOLMint.toString();
const _baseRewardAmount = rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime);
const { account: ownerRewardAccount, instructionParams: ownerRewardAccountIns } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(rewardInfo.mint.address),
mint: new PublicKey(rewardInfo.mint.address),
notUseTokenAccount: !!rewardMintUseSOLBalance,
skipCloseAccount: !rewardMintUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: rewardMintUseSOLBalance
? {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: new BN(
new Decimal(_baseRewardAmount.toFixed(0)).gte(_baseRewardAmount)
? _baseRewardAmount.toFixed(0)
: _baseRewardAmount.add(1).toFixed(0),
),
}
: undefined,
associatedOnly: rewardMintUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardAccountIns && txBuilder.addInstruction(ownerRewardAccountIns);
if (!ownerRewardAccount)
this.logAndCreateError("no money", "ownerRewardAccount", this.scope.account.tokenAccountRawInfos);
const poolKeys = await this.getClmmPoolKeys(poolInfo.id);
const insInfo = ClmmInstrument.initRewardInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccount: ownerRewardAccount!,
},
rewardInfo: {
programId: new PublicKey(rewardInfo.mint.programId),
mint: new PublicKey(rewardInfo.mint.address),
openTime: rewardInfo.openTime,
endTime: rewardInfo.endTime,
emissionsPerSecondX64: MathUtil.decimalToX64(rewardInfo.perSecond),
},
});
txBuilder.addInstruction(insInfo);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
return txBuilder.versionBuild<InitRewardExtInfo>({
txVersion,
extInfo: { address: insInfo.address },
}) as Promise<MakeTxData<T, InitRewardExtInfo>>;
}
public async initRewards<T extends TxVersion>({
poolInfo,
poolKeys: propPoolKeys,
ownerInfo,
rewardInfos,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
}: InitRewardsParams<T>): Promise<MakeTxData<T, { address: Record<string, PublicKey> }>> {
for (const rewardInfo of rewardInfos) {
if (rewardInfo.endTime <= rewardInfo.openTime)
this.logAndCreateError("reward time error", "rewardInfo", rewardInfo);
}
const txBuilder = this.createTxBuilder(feePayer);
let address: Record<string, PublicKey> = {};
for (const rewardInfo of rewardInfos) {
const rewardMintUseSOLBalance = ownerInfo.useSOLBalance && rewardInfo.mint.address === WSOLMint.toString();
const _baseRewardAmount = rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime);
const { account: ownerRewardAccount, instructionParams: ownerRewardAccountIns } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(rewardInfo.mint.programId),
mint: new PublicKey(rewardInfo.mint.address),
notUseTokenAccount: !!rewardMintUseSOLBalance,
skipCloseAccount: !rewardMintUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: rewardMintUseSOLBalance
? {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: new BN(
new Decimal(_baseRewardAmount.toFixed(0)).gte(_baseRewardAmount)
? _baseRewardAmount.toFixed(0)
: _baseRewardAmount.add(1).toFixed(0),
),
}
: undefined,
associatedOnly: rewardMintUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardAccountIns && txBuilder.addInstruction(ownerRewardAccountIns);
if (!ownerRewardAccount)
this.logAndCreateError("no money", "ownerRewardAccount", this.scope.account.tokenAccountRawInfos);
const poolKeys = propPoolKeys ?? (await this.getClmmPoolKeys(poolInfo.id));
const insInfo = ClmmInstrument.initRewardInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccount: ownerRewardAccount!,
},
rewardInfo: {
programId: new PublicKey(rewardInfo.mint.programId),
mint: new PublicKey(rewardInfo.mint.address),
openTime: rewardInfo.openTime,
endTime: rewardInfo.endTime,
emissionsPerSecondX64: MathUtil.decimalToX64(rewardInfo.perSecond),
},
});
address = {
...address,
...insInfo.address,
};
txBuilder.addInstruction(insInfo);
}
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild({
txVersion,
extInfo: { address },
}) as Promise<MakeTxData<T, { address: Record<string, PublicKey> }>>;
}
public async setReward<T extends TxVersion>({
poolInfo,
ownerInfo,
rewardInfo,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
}: SetRewardParams<T>): Promise<MakeTxData<T, { address: Record<string, PublicKey> }>> {
if (rewardInfo.endTime <= rewardInfo.openTime)
this.logAndCreateError("reward time error", "rewardInfo", rewardInfo);
const txBuilder = this.createTxBuilder(feePayer);
const rewardMintUseSOLBalance = ownerInfo.useSOLBalance && rewardInfo.mint.equals(WSOLMint);
const { account: ownerRewardAccount, instructionParams: ownerRewardIns } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: rewardInfo.programId,
mint: rewardInfo.mint,
notUseTokenAccount: rewardMintUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: rewardMintUseSOLBalance
? {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: new BN(
new Decimal(rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime).toFixed(0)).gte(
rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime),
)
? rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime).toFixed(0)
: rewardInfo.perSecond
.mul(rewardInfo.endTime - rewardInfo.openTime)
.add(1)
.toFixed(0),
),
}
: undefined,
associatedOnly: rewardMintUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardIns && txBuilder.addInstruction(ownerRewardIns);
if (!ownerRewardAccount)
this.logAndCreateError("no money", "ownerRewardAccount", this.scope.account.tokenAccountRawInfos);
const poolKeys = await this.getClmmPoolKeys(poolInfo.id);
const insInfo = ClmmInstrument.setRewardInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccount: ownerRewardAccount!,
},
rewardInfo: {
mint: rewardInfo.mint,
openTime: rewardInfo.openTime,
endTime: rewardInfo.endTime,
emissionsPerSecondX64: MathUtil.decimalToX64(rewardInfo.perSecond),
},
});
txBuilder.addInstruction(insInfo);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ address: Record<string, PublicKey> }>({
txVersion,
extInfo: { address: insInfo.address },
}) as Promise<MakeTxData<T, { address: Record<string, PublicKey> }>>;
}
public async setRewards<T extends TxVersion>({
poolInfo,
poolKeys: propPoolKeys,
ownerInfo,
rewardInfos,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
}: SetRewardsParams<T>): Promise<MakeTxData<T, { address: Record<string, PublicKey> }>> {
const txBuilder = this.createTxBuilder(feePayer);
let address: Record<string, PublicKey> = {};
for (const rewardInfo of rewardInfos) {
if (rewardInfo.endTime <= rewardInfo.openTime)
this.logAndCreateError("reward time error", "rewardInfo", rewardInfo);
const rewardMintUseSOLBalance = ownerInfo.useSOLBalance && rewardInfo.mint.address === WSOLMint.toString();
const { account: ownerRewardAccount, instructionParams: ownerRewardIns } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(rewardInfo.mint.programId),
mint: new PublicKey(rewardInfo.mint.address),
notUseTokenAccount: rewardMintUseSOLBalance,
owner: this.scope.ownerPubKey,
createInfo: rewardMintUseSOLBalance
? {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: new BN(
new Decimal(rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime).toFixed(0)).gte(
rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime),
)
? rewardInfo.perSecond.mul(rewardInfo.endTime - rewardInfo.openTime).toFixed(0)
: rewardInfo.perSecond
.mul(rewardInfo.endTime - rewardInfo.openTime)
.add(1)
.toFixed(0),
),
}
: undefined,
associatedOnly: rewardMintUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardIns && txBuilder.addInstruction(ownerRewardIns);
if (!ownerRewardAccount)
this.logAndCreateError("no money", "ownerRewardAccount", this.scope.account.tokenAccountRawInfos);
const poolKeys = propPoolKeys ?? (await this.getClmmPoolKeys(poolInfo.id));
const insInfo = ClmmInstrument.setRewardInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccount: ownerRewardAccount!,
},
rewardInfo: {
mint: new PublicKey(rewardInfo.mint.address),
openTime: rewardInfo.openTime,
endTime: rewardInfo.endTime,
emissionsPerSecondX64: MathUtil.decimalToX64(rewardInfo.perSecond),
},
});
txBuilder.addInstruction(insInfo);
address = {
...address,
...insInfo.address,
};
}
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ address: Record<string, PublicKey> }>({
txVersion,
extInfo: { address },
}) as Promise<MakeTxData<T, { address: Record<string, PublicKey> }>>;
}
public async collectReward<T extends TxVersion>({
poolInfo,
ownerInfo,
rewardMint,
associatedOnly = true,
checkCreateATAOwner = false,
computeBudgetConfig,
txTipConfig,
txVersion,
feePayer,
}: CollectRewardParams<T>): Promise<MakeTxData<{ address: Record<string, PublicKey> }>> {
const rewardInfo = poolInfo!.rewardDefaultInfos.find((i) => i.mint.address === rewardMint.toString());
if (!rewardInfo) this.logAndCreateError("reward mint error", "not found reward mint", rewardMint);
const txBuilder = this.createTxBuilder(feePayer);
const rewardMintUseSOLBalance = ownerInfo.useSOLBalance && rewardMint.equals(WSOLMint);
const { account: ownerRewardAccount, instructionParams: ownerRewardIns } =
await this.scope.account.getOrCreateTokenAccount({
tokenProgram: new PublicKey(rewardInfo!.mint.programId),
mint: rewardMint,
notUseTokenAccount: rewardMintUseSOLBalance,
owner: this.scope.ownerPubKey,
skipCloseAccount: !rewardMintUseSOLBalance,
createInfo: {
payer: ownerInfo.feePayer || this.scope.ownerPubKey,
amount: 0,
},
associatedOnly: rewardMintUseSOLBalance ? false : associatedOnly,
checkCreateATAOwner,
});
ownerRewardIns && txBuilder.addInstruction(ownerRewardIns);
if (!ownerRewardAccount)
this.logAndCreateError("no money", "ownerRewardAccount", this.scope.account.tokenAccountRawInfos);
const poolKeys = await this.getClmmPoolKeys(poolInfo.id);
const insInfo = ClmmInstrument.collectRewardInstructions({
poolInfo,
poolKeys,
ownerInfo: {
wallet: this.scope.ownerPubKey,
tokenAccount: ownerRewardAccount!,
},
rewardMint,
});
txBuilder.addInstruction(insInfo);
txBuilder.addCustomComputeBudget(computeBudgetConfig);
txBuilder.addTipInstruction(txTipConfig);
return txBuilder.versionBuild<{ address: Record<string, PublicKey> }>({
txVersion,
extInfo: { address: insInfo.address },
}) as Promise<MakeTxData<{ address: Record<string, PublicKey> }>>;
}
public async collectRewards({
poolInfo,