@raydium-io/raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
2,567 lines • 86.3 kB
text/typescript
import { ApiV3PoolInfoConcentratedItem, ApiV3Token, ClmmKeys } from "@/api/type";
import { InstructionType, MEMO_PROGRAM_ID, METADATA_PROGRAM_ID, RENT_PROGRAM_ID, getATAAddress } from "@/common";
import { createLogger } from "@/common/logger";
import { bool, s32, struct, u128, u16, u64, u8 } from "@/marshmallow";
import { ReturnTypeMakeInstructions } from "@/raydium/type";
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, Keypair, PublicKey, Signer, SystemProgram, TransactionInstruction } from "@solana/web3.js";
import BN from "bn.js";
import { ObservationLayout, PersonalPositionLayout } from "./layout";
import {
getPdaExBitmapAccount,
getPdaLockClPositionIdV2,
getPdaLockPositionId,
getPdaMetadataKey,
getPdaObservationAccount,
getPdaOperationAccount,
getPdaPermission,
getPdaPermissionedPoolId,
getPdaPersonalPositionAddress,
getPdaPoolId,
getPdaPoolRewardVaultId,
getPdaPoolVaultId,
getPdaProtocolPositionAddress,
getPdaTickArrayAddress,
} from "./libraries/pda";
import {
ClmmLockAddress,
ClmmPoolPersonalPosition,
ClosePositionExtInfo,
InitRewardExtInfo,
ManipulateLiquidityExtInfo,
OpenPositionFromBaseExtInfo,
OpenPositionFromLiquidityExtInfo,
SimpleClmmPoolInfo,
} from "./type";
import { sha256 } from "js-sha256";
import { PoolUtil } from "./libraries";
import { BN_ZERO } from "./libraries/constants";
import { TickArrayUtil } from "./libraries/tickArrayUtil";
function getAnchorByte(ixName: string): Buffer {
const preimage = `global:${ixName}`;
return Buffer.from(sha256.digest(preimage)).slice(0, 8);
}
ObservationLayout.span; // do not delete this line
const logger = createLogger("Raydium_Clmm");
const insId = {
createPool: getAnchorByte("create_pool"),
createCustomizablePool: getAnchorByte("create_customizable_pool"), // todo
createPermissionedPool: getAnchorByte("create_permissioned_pool"),
openPositionV2: getAnchorByte("open_position_v2"),
openPositionWithToken22Nft: getAnchorByte("open_position_with_token22_nft"),
closePosition: getAnchorByte("close_position"),
increaseLiquidityV2: getAnchorByte("increase_liquidity_v2"),
decreaseLiquidityV2: getAnchorByte("decrease_liquidity_v2"),
initializeReward: getAnchorByte("initialize_reward"),
setRewardParams: getAnchorByte("set_reward_params"),
updateRewardInfos: getAnchorByte("update_reward_infos"),
collectRemainingRewards: getAnchorByte("collect_remaining_rewards"),
swapV2: getAnchorByte("swap_v2"),
openLimitOrder: getAnchorByte("open_limit_order"), // todo
increaseLimitOrder: getAnchorByte("increase_limit_order"), // todo
decreaseLimitOrder: getAnchorByte("decrease_limit_order"), // todo
settleLimitOrder: getAnchorByte("settle_limit_order"), // todo
closeLimitOrder: getAnchorByte("close_limit_order"), // todo
};
const lockInsDataBuf = [188, 37, 179, 131, 82, 150, 84, 73];
const lockHarvestInsDataBuf = [16, 72, 250, 198, 14, 162, 212, 19];
interface CreatePoolInstruction {
connection: Connection;
programId: PublicKey;
owner: PublicKey;
mintA: ApiV3Token;
mintB: ApiV3Token;
ammConfigId: PublicKey;
initialPriceX64: BN;
forerunCreate?: boolean;
extendMintAccount?: PublicKey[];
}
export class ClmmInstrument {
static createPoolInstruction(
programId: PublicKey,
poolId: PublicKey,
poolCreator: PublicKey,
ammConfigId: PublicKey,
observationId: PublicKey,
mintA: PublicKey,
vaultA: PublicKey,
mintProgramIdA: PublicKey,
mintB: PublicKey,
vaultB: PublicKey,
mintProgramIdB: PublicKey,
tickArrayBitmap: PublicKey,
sqrtPriceX64: BN,
supperMintEx: PublicKey[] | undefined,
): TransactionInstruction {
const dataLayout = struct([u128("sqrtPriceX64"), u64("startTime")]);
const keys = [
{ pubkey: poolCreator, isSigner: true, isWritable: true },
{ pubkey: ammConfigId, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: observationId, isSigner: false, isWritable: true },
{ pubkey: tickArrayBitmap, isSigner: false, isWritable: true },
{ pubkey: mintProgramIdA, isSigner: false, isWritable: false },
{ pubkey: mintProgramIdB, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
...(supperMintEx ?? []).map((i) => ({ pubkey: i, isSigner: false, isWritable: false })),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ sqrtPriceX64, startTime: BN_ZERO }, data);
const aData = Buffer.from([...insId.createPool, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static createCustomizablePoolInstruction(
programId: PublicKey,
poolId: PublicKey,
poolCreator: PublicKey,
ammConfig: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
observationId: PublicKey,
tickArrayBitmap: PublicKey,
mintProgramIdA: PublicKey,
mintProgramIdB: PublicKey,
sqrtPriceX64: BN,
collectFeeOn: number, // new
supperMintEx: PublicKey[],
dynamicFeeConfig?: PublicKey, // new
): TransactionInstruction {
const dataLayout = struct([u128("sqrtPriceX64"), u8("collectFeeOn"), bool("enableDynamicFee")]);
const keys = [
{ pubkey: poolCreator, isSigner: true, isWritable: true },
{ pubkey: ammConfig, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: observationId, isSigner: false, isWritable: true },
{ pubkey: tickArrayBitmap, isSigner: false, isWritable: true },
{ pubkey: mintProgramIdA, isSigner: false, isWritable: false },
{ pubkey: mintProgramIdB, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
...supperMintEx.map((i) => ({ pubkey: i, isSigner: false, isWritable: false })),
...(dynamicFeeConfig ? [{ pubkey: dynamicFeeConfig, isSigner: false, isWritable: false }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ sqrtPriceX64, collectFeeOn, enableDynamicFee: dynamicFeeConfig !== undefined }, data);
const aData = Buffer.from([...insId.createCustomizablePool, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static createPermissionedPoolInstruction(
programId: PublicKey,
payer: PublicKey,
poolCreator: PublicKey,
permission: PublicKey,
ammConfig: PublicKey,
poolId: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
observationId: PublicKey,
tickArrayBitmap: PublicKey,
mintProgramIdA: PublicKey,
mintProgramIdB: PublicKey,
sqrtPriceX64: BN,
collectFeeOn: number,
seedIndex: number,
supperMintEx: PublicKey[],
dynamicFeeConfig?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([u128("sqrtPriceX64"), u8("collectFeeOn"), bool("enableDynamicFee"), u16("seedIndex")]);
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: poolCreator, isSigner: false, isWritable: false },
{ pubkey: permission, isSigner: false, isWritable: false },
{ pubkey: ammConfig, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: observationId, isSigner: false, isWritable: true },
{ pubkey: tickArrayBitmap, isSigner: false, isWritable: true },
{ pubkey: mintProgramIdA, isSigner: false, isWritable: false },
{ pubkey: mintProgramIdB, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
...supperMintEx.map((i) => ({ pubkey: i, isSigner: false, isWritable: false })),
...(dynamicFeeConfig ? [{ pubkey: dynamicFeeConfig, isSigner: false, isWritable: false }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{ sqrtPriceX64, collectFeeOn, enableDynamicFee: dynamicFeeConfig !== undefined, seedIndex },
data,
);
const aData = Buffer.from([...insId.createPermissionedPool, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static initializeRewardInstruction(
programId: PublicKey,
rewardFunder: PublicKey,
poolId: PublicKey,
operationId: PublicKey,
ammConfigId: PublicKey,
funderTokenAccount: PublicKey,
tokenProgramId: PublicKey,
rewardTokenMint: PublicKey,
rewardTokenVault: PublicKey,
openTime: BN,
endTime: BN,
emissionsPerSecondX64: BN,
): TransactionInstruction {
const dataLayout = struct([u64("openTime"), u64("endTime"), u128("emissionsPerSecondX64")]);
const keys = [
{ pubkey: rewardFunder, isSigner: true, isWritable: true },
{ pubkey: funderTokenAccount, isSigner: false, isWritable: true },
{ pubkey: ammConfigId, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: operationId, isSigner: false, isWritable: false },
{ pubkey: rewardTokenMint, isSigner: false, isWritable: false },
{ pubkey: rewardTokenVault, isSigner: false, isWritable: true },
{ pubkey: tokenProgramId, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ openTime, endTime, emissionsPerSecondX64 }, data);
const aData = Buffer.from([...insId.initializeReward, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static updateRewardInfosInstruction(programId: PublicKey, poolId: PublicKey): TransactionInstruction {
const keys = [{ pubkey: poolId, isSigner: false, isWritable: true }];
const aData = Buffer.from([...insId.updateRewardInfos]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static setRewardParamsInstruction(
programId: PublicKey,
authority: PublicKey,
poolId: PublicKey,
operationId: PublicKey,
ammConfigId: PublicKey,
ownerTokenAccount: PublicKey,
rewardVault: PublicKey,
rewardMint: PublicKey,
rewardIndex: number,
openTime: BN,
endTime: BN,
emissionsPerSecondX64: BN,
): TransactionInstruction {
const dataLayout = struct([u8("rewardIndex"), u128("emissionsPerSecondX64"), u64("openTime"), u64("endTime")]);
const keys = [
{ pubkey: authority, isSigner: true, isWritable: false },
{ pubkey: ammConfigId, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: operationId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: rewardVault, isSigner: false, isWritable: true },
{ pubkey: ownerTokenAccount, isSigner: false, isWritable: true },
{ pubkey: rewardMint, isSigner: false, isWritable: true },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ rewardIndex, emissionsPerSecondX64, openTime, endTime }, data);
const aData = Buffer.from([...insId.setRewardParams, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static closePositionInstruction(
programId: PublicKey,
nftOwner: PublicKey,
positionNftMint: PublicKey,
positionNftAccount: PublicKey,
personalPosition: PublicKey,
nft2022: boolean,
poolId?: PublicKey,
): TransactionInstruction {
const keys = [
{ pubkey: nftOwner, isSigner: true, isWritable: true },
{ pubkey: positionNftMint, isSigner: false, isWritable: true },
{ pubkey: positionNftAccount, isSigner: false, isWritable: true },
{ pubkey: personalPosition, isSigner: false, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: nft2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
];
if (poolId) keys.push({ pubkey: poolId, isSigner: false, isWritable: true });
const aData = Buffer.from([...insId.closePosition]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static openPositionV2Instruction(
programId: PublicKey,
payer: PublicKey,
poolId: PublicKey,
positionNftOwner: PublicKey,
positionNftMint: PublicKey,
positionNftAccount: PublicKey,
metadataAccount: PublicKey,
protocolPosition: PublicKey,
tickArrayLower: PublicKey,
tickArrayUpper: PublicKey,
personalPosition: PublicKey,
ownerVaultA: PublicKey,
ownerVaultB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
tickLower: number,
tickUpper: number,
tickArrayLowerStart: number,
tickArrayUpperStart: number,
liquidity: BN,
amountMaxA: BN,
amountMaxB: BN,
withMetadata: boolean,
baseFlag: boolean | null,
exTickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([
s32("tickLower"),
s32("tickUpper"),
s32("tickArrayLowerStart"),
s32("tickArrayUpperStart"),
u128("liquidity"),
u64("amountMaxA"),
u64("amountMaxB"),
bool("withMetadata"),
u8("optionBaseFlag"),
bool("baseFlag"),
]);
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: positionNftOwner, isSigner: false, isWritable: false },
{ pubkey: positionNftMint, isSigner: true, isWritable: true },
{ pubkey: positionNftAccount, isSigner: false, isWritable: true },
{ pubkey: metadataAccount, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: false },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: personalPosition, isSigner: false, isWritable: true },
{ pubkey: ownerVaultA, isSigner: false, isWritable: true },
{ pubkey: ownerVaultB, isSigner: false, isWritable: true },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
tickLower,
tickUpper,
tickArrayLowerStart,
tickArrayUpperStart,
liquidity,
amountMaxA,
amountMaxB,
withMetadata,
optionBaseFlag: baseFlag !== null ? 1 : 0,
baseFlag: baseFlag ?? false,
},
data,
);
const aData = Buffer.from([...insId.openPositionV2, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static increaseLiquidityV2Instruction(
programId: PublicKey,
nftOwner: PublicKey,
nftAccount: PublicKey,
personalPosition: PublicKey,
poolId: PublicKey,
protocolPosition: PublicKey,
tickArrayLower: PublicKey,
tickArrayUpper: PublicKey,
ownerVaultA: PublicKey,
ownerVaultB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
liquidity: BN,
amountMaxA: BN,
amountMaxB: BN,
baseFlag: boolean | null,
exTickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([
u128("liquidity"),
u64("amountMaxA"),
u64("amountMaxB"),
u8("optionBaseFlag"),
bool("baseFlag"),
]);
const keys = [
{ pubkey: nftOwner, isSigner: true, isWritable: false },
{ pubkey: nftAccount, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: false },
{ pubkey: personalPosition, isSigner: false, isWritable: true },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: ownerVaultA, isSigner: false, isWritable: true },
{ pubkey: ownerVaultB, isSigner: false, isWritable: true },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
liquidity,
amountMaxA,
amountMaxB,
optionBaseFlag: baseFlag !== null ? 1 : 0,
baseFlag: baseFlag ?? false,
},
data,
);
const aData = Buffer.from([...insId.increaseLiquidityV2, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static decreaseLiquidityV2Instruction(
programId: PublicKey,
nftOwner: PublicKey,
nftAccount: PublicKey,
personalPosition: PublicKey,
poolId: PublicKey,
protocolPosition: PublicKey,
tickArrayLower: PublicKey,
tickArrayUpper: PublicKey,
ownerVaultA: PublicKey,
ownerVaultB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
rewardAccounts: {
poolRewardVault: PublicKey;
ownerRewardVault: PublicKey;
rewardMint: PublicKey;
}[],
liquidity: BN,
amountMinA: BN,
amountMinB: BN,
exTickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([u128("liquidity"), u64("amountMinA"), u64("amountMinB")]);
const keys = [
{ pubkey: nftOwner, isSigner: true, isWritable: false },
{ pubkey: nftAccount, isSigner: false, isWritable: false },
{ pubkey: personalPosition, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: false },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: ownerVaultA, isSigner: false, isWritable: true },
{ pubkey: ownerVaultB, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
...rewardAccounts
.map((i) => [
{ pubkey: i.poolRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.ownerRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.rewardMint, isSigner: false, isWritable: false },
])
.flat(),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ liquidity, amountMinA, amountMinB }, data);
const aData = Buffer.from([...insId.decreaseLiquidityV2, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static swapV2Instruction(
programId: PublicKey,
payer: PublicKey,
poolId: PublicKey,
ammConfig: PublicKey,
inputTokenAccount: PublicKey,
outputTokenAccount: PublicKey,
inputVault: PublicKey,
outputVault: PublicKey,
inputTokenMint: PublicKey,
outputTokenMint: PublicKey,
tickArray: PublicKey[],
observationId: PublicKey,
amount: BN,
otherAmountThreshold: BN,
sqrtPriceLimitX64: BN,
isBaseInput: boolean,
tickArrayBitmapExtension?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([
u64("amount"),
u64("otherAmountThreshold"),
u128("sqrtPriceLimitX64"),
bool("isBaseInput"),
]);
const keys = [
{ pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: ammConfig, isSigner: false, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: inputVault, isSigner: false, isWritable: true },
{ pubkey: outputVault, isSigner: false, isWritable: true },
{ pubkey: observationId, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: inputTokenMint, isSigner: false, isWritable: false },
{ pubkey: outputTokenMint, isSigner: false, isWritable: false },
...(tickArrayBitmapExtension ? [{ pubkey: tickArrayBitmapExtension, isSigner: false, isWritable: true }] : []),
...tickArray.map((i) => ({ pubkey: i, isSigner: false, isWritable: true })),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ amount, otherAmountThreshold, sqrtPriceLimitX64, isBaseInput }, data);
const aData = Buffer.from([...insId.swapV2, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static collectRemainingRewardsInstruction(
programId: PublicKey,
rewardFunder: PublicKey,
poolId: PublicKey,
funderTokenAccount: PublicKey,
rewardTokenVault: PublicKey,
rewardMint: PublicKey,
rewardIndex: number,
): TransactionInstruction {
const dataLayout = struct([u8("rewardIndex")]);
const keys = [
{ pubkey: rewardFunder, isSigner: true, isWritable: false },
{ pubkey: funderTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: rewardTokenVault, isSigner: false, isWritable: true },
{ pubkey: rewardMint, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ rewardIndex }, data);
const aData = Buffer.from([...insId.collectRemainingRewards, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static openPositionWithToken22NftInstruction(
programId: PublicKey,
payer: PublicKey,
poolId: PublicKey,
positionNftOwner: PublicKey,
positionNftMint: PublicKey,
positionNftAccount: PublicKey,
protocolPosition: PublicKey,
tickArrayLower: PublicKey,
tickArrayUpper: PublicKey,
personalPosition: PublicKey,
ownerVaultA: PublicKey,
ownerVaultB: PublicKey,
vaultA: PublicKey,
vaultB: PublicKey,
mintA: PublicKey,
mintB: PublicKey,
tickLower: number,
tickUpper: number,
tickArrayLowerStart: number,
tickArrayUpperStart: number,
liquidity: BN,
amountMaxA: BN,
amountMaxB: BN,
withMetadata: boolean,
baseFlag: boolean | null,
exTickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([
s32("tickLower"),
s32("tickUpper"),
s32("tickArrayLowerStart"),
s32("tickArrayUpperStart"),
u128("liquidity"),
u64("amountMaxA"),
u64("amountMaxB"),
bool("withMetadata"),
u8("optionBaseFlag"),
bool("baseFlag"),
]);
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: positionNftOwner, isSigner: false, isWritable: false },
{ pubkey: positionNftMint, isSigner: true, isWritable: true },
{ pubkey: positionNftAccount, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: false },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: personalPosition, isSigner: false, isWritable: true },
{ pubkey: ownerVaultA, isSigner: false, isWritable: true },
{ pubkey: ownerVaultB, isSigner: false, isWritable: true },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
tickLower,
tickUpper,
tickArrayLowerStart,
tickArrayUpperStart,
liquidity,
amountMaxA,
amountMaxB,
withMetadata,
optionBaseFlag: baseFlag !== null ? 1 : 0,
baseFlag: baseFlag ?? false,
},
data,
);
const aData = Buffer.from([...insId.openPositionWithToken22Nft, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static openLimitOrderInstruction(
programId: PublicKey,
payer: PublicKey,
poolId: PublicKey,
tickArray: PublicKey,
limitOrderNonce: PublicKey,
limitOrder: PublicKey,
inputTokenAccount: PublicKey,
outputTokenAccount: PublicKey,
inputVault: PublicKey,
outputVault: PublicKey,
inputVaultMint: PublicKey,
outputVaultMint: PublicKey,
inputTokenProgram: PublicKey,
nonceIndex: number,
zeroForOne: boolean,
tickIndex: number,
amount: BN,
tickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([u8("nonceIndex"), bool("zeroForOne"), s32("tickIndex"), u64("amount")]);
const keys = [
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: tickArray, isSigner: false, isWritable: true },
{ pubkey: limitOrderNonce, isSigner: false, isWritable: true },
{ pubkey: limitOrder, isSigner: false, isWritable: true },
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: inputVault, isSigner: false, isWritable: true },
{ pubkey: outputVault, isSigner: false, isWritable: true },
{ pubkey: inputVaultMint, isSigner: false, isWritable: false },
{ pubkey: outputVaultMint, isSigner: false, isWritable: false },
{ pubkey: inputTokenProgram, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
...(tickArrayBitmap ? [{ pubkey: tickArrayBitmap, isSigner: false, isWritable: true }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ nonceIndex, zeroForOne, tickIndex, amount }, data);
const aData = Buffer.from([...insId.openLimitOrder, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static increaseLimitOrderInstruction(
programId: PublicKey,
owner: PublicKey,
poolId: PublicKey,
tickArray: PublicKey,
limitOrder: PublicKey,
inputTokenAccount: PublicKey,
inputVault: PublicKey,
inputVaultMint: PublicKey,
inputTokenProgram: PublicKey,
amount: BN,
): TransactionInstruction {
const dataLayout = struct([u64("amount")]);
const keys = [
{ pubkey: owner, isSigner: true, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: tickArray, isSigner: false, isWritable: true },
{ pubkey: limitOrder, isSigner: false, isWritable: true },
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: inputVault, isSigner: false, isWritable: true },
{ pubkey: inputVaultMint, isSigner: false, isWritable: false },
{ pubkey: inputTokenProgram, isSigner: false, isWritable: false },
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ amount }, data);
const aData = Buffer.from([...insId.increaseLimitOrder, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static decreaseLimitOrderInstruction(
programId: PublicKey,
owner: PublicKey,
poolId: PublicKey,
tickArray: PublicKey,
limitOrder: PublicKey,
inputTokenAccount: PublicKey,
outputTokenAccount: PublicKey,
inputVault: PublicKey,
outputVault: PublicKey,
inputVaultMint: PublicKey,
outputVaultMint: PublicKey,
amount: BN,
amountMin: BN,
tickArrayBitmap?: PublicKey,
): TransactionInstruction {
const dataLayout = struct([u64("amount"), u64("amountMin")]);
const keys = [
{ pubkey: owner, isSigner: true, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: tickArray, isSigner: false, isWritable: true },
{ pubkey: limitOrder, isSigner: false, isWritable: true },
{ pubkey: inputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: inputVault, isSigner: false, isWritable: true },
{ pubkey: outputVault, isSigner: false, isWritable: true },
{ pubkey: inputVaultMint, isSigner: false, isWritable: false },
{ pubkey: outputVaultMint, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
...(tickArrayBitmap ? [{ pubkey: tickArrayBitmap, isSigner: false, isWritable: true }] : []),
];
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode({ amount, amountMin }, data);
const aData = Buffer.from([...insId.decreaseLimitOrder, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static settleLimitOrderInstruction(
programId: PublicKey,
signer: PublicKey,
poolId: PublicKey,
tickArray: PublicKey,
limitOrder: PublicKey,
outputTokenAccount: PublicKey,
outputVault: PublicKey,
outputVaultMint: PublicKey,
outputTokenProgram: PublicKey,
): TransactionInstruction {
const keys = [
{ pubkey: signer, isSigner: true, isWritable: false },
{ pubkey: poolId, isSigner: false, isWritable: false },
{ pubkey: tickArray, isSigner: false, isWritable: true },
{ pubkey: limitOrder, isSigner: false, isWritable: true },
{ pubkey: outputTokenAccount, isSigner: false, isWritable: true },
{ pubkey: outputVault, isSigner: false, isWritable: true },
{ pubkey: outputVaultMint, isSigner: false, isWritable: false },
{ pubkey: outputTokenProgram, isSigner: false, isWritable: false },
];
const aData = Buffer.from([...insId.settleLimitOrder]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static closeLimitOrderInstruction(
programId: PublicKey,
signer: PublicKey,
rentReceiver: PublicKey,
limitOrder: PublicKey,
): TransactionInstruction {
const keys = [
{ pubkey: signer, isSigner: true, isWritable: false },
{ pubkey: rentReceiver, isSigner: false, isWritable: true },
{ pubkey: limitOrder, isSigner: false, isWritable: true },
];
const aData = Buffer.from([...insId.closeLimitOrder]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static createPoolInstructions(props: CreatePoolInstruction): ReturnTypeMakeInstructions<{
poolId: PublicKey;
observationId: PublicKey;
exBitmapAccount: PublicKey;
mintA: PublicKey;
mintB: PublicKey;
mintAProgram: PublicKey;
mintBProgram: PublicKey;
mintAVault: PublicKey;
mintBVault: PublicKey;
}> {
const { programId, owner, mintA, mintB, ammConfigId, initialPriceX64, extendMintAccount } = props;
const [mintAAddress, mintBAddress] = [new PublicKey(mintA.address), new PublicKey(mintB.address)];
const [mintAProgram, mintBProgram] = [
new PublicKey(mintA.programId || TOKEN_PROGRAM_ID),
new PublicKey(mintB.programId || TOKEN_PROGRAM_ID),
];
const { publicKey: poolId } = getPdaPoolId(programId, ammConfigId, mintAAddress, mintBAddress);
const { publicKey: observationId } = getPdaObservationAccount(programId, poolId);
const { publicKey: mintAVault } = getPdaPoolVaultId(programId, poolId, mintAAddress);
const { publicKey: mintBVault } = getPdaPoolVaultId(programId, poolId, mintBAddress);
const exBitmapAccount = getPdaExBitmapAccount(programId, poolId).publicKey;
const ins = [
this.createPoolInstruction(
programId,
poolId,
owner,
ammConfigId,
observationId,
mintAAddress,
mintAVault,
mintAProgram,
mintBAddress,
mintBVault,
mintBProgram,
exBitmapAccount,
initialPriceX64,
extendMintAccount,
),
];
return {
signers: [],
instructions: ins,
instructionTypes: [InstructionType.CreateAccount, InstructionType.ClmmCreatePool],
address: {
poolId,
observationId,
exBitmapAccount,
mintA: mintAAddress,
mintB: mintBAddress,
mintAProgram,
mintBProgram,
mintAVault,
mintBVault,
},
lookupTableAddress: [],
};
}
static createPermissionedPoolInstructions(
props: CreatePoolInstruction & { payer: PublicKey; seedIndex: number },
): ReturnTypeMakeInstructions<{
poolId: PublicKey;
observationId: PublicKey;
exBitmapAccount: PublicKey;
mintA: PublicKey;
mintB: PublicKey;
mintAProgram: PublicKey;
mintBProgram: PublicKey;
mintAVault: PublicKey;
mintBVault: PublicKey;
permission: PublicKey;
}> {
const { programId, owner, mintA, mintB, ammConfigId, initialPriceX64, extendMintAccount, payer, seedIndex } = props;
const [mintAAddress, mintBAddress] = [new PublicKey(mintA.address), new PublicKey(mintB.address)];
const [mintAProgram, mintBProgram] = [
new PublicKey(mintA.programId || TOKEN_PROGRAM_ID),
new PublicKey(mintB.programId || TOKEN_PROGRAM_ID),
];
const { publicKey: poolId } = getPdaPermissionedPoolId(
programId,
ammConfigId,
mintAAddress,
mintBAddress,
seedIndex,
);
const { publicKey: permission } = getPdaPermission(programId, payer);
const { publicKey: observationId } = getPdaObservationAccount(programId, poolId);
const { publicKey: mintAVault } = getPdaPoolVaultId(programId, poolId, mintAAddress);
const { publicKey: mintBVault } = getPdaPoolVaultId(programId, poolId, mintBAddress);
const exBitmapAccount = getPdaExBitmapAccount(programId, poolId).publicKey;
const ins = [
this.createPoolInstruction(
programId,
poolId,
owner,
ammConfigId,
observationId,
mintAAddress,
mintAVault,
mintAProgram,
mintBAddress,
mintBVault,
mintBProgram,
exBitmapAccount,
initialPriceX64,
extendMintAccount,
),
];
return {
signers: [],
instructions: ins,
instructionTypes: [InstructionType.CreateAccount, InstructionType.ClmmCreatePool],
address: {
poolId,
observationId,
exBitmapAccount,
mintA: mintAAddress,
mintB: mintBAddress,
mintAProgram,
mintBProgram,
mintAVault,
mintBVault,
permission,
},
lookupTableAddress: [],
};
}
static async openPositionInstructions({
poolInfo,
poolKeys,
ownerInfo,
tickLower,
tickUpper,
liquidity,
amountMaxA,
amountMaxB,
base,
withMetadata,
getEphemeralSigners,
nft2022,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerInfo: {
feePayer: PublicKey;
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
tickLower: number;
tickUpper: number;
liquidity: BN;
amountMaxA: BN;
amountMaxB: BN;
base: "MintA" | "MintB" | null;
withMetadata: "create" | "no-create";
getEphemeralSigners?: (k: number) => any;
nft2022?: boolean;
}): Promise<ReturnTypeMakeInstructions> {
const signers: Signer[] = [];
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
let nftMintAccount;
if (getEphemeralSigners) {
nftMintAccount = new PublicKey((await getEphemeralSigners(1))[0]);
} else {
const _k = Keypair.generate();
signers.push(_k);
nftMintAccount = _k.publicKey;
}
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(tickLower, poolInfo.config.tickSpacing);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(tickUpper, poolInfo.config.tickSpacing);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_PROGRAM_ID);
const { publicKey: metadataAccount } = getPdaMetadataKey(nftMintAccount);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, nftMintAccount);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(programId, id, tickLower, tickUpper);
const ins = nft2022
? this.openPositionWithToken22NftInstruction(
programId,
ownerInfo.feePayer,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
liquidity,
amountMaxA,
amountMaxB,
withMetadata === "create",
base ? base === "MintA" : base,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
)
: this.openPositionV2Instruction(
programId,
ownerInfo.feePayer,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
metadataAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
liquidity,
amountMaxA,
amountMaxB,
withMetadata === "create",
null,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
);
return {
signers,
instructions: [ins],
instructionTypes: [InstructionType.ClmmOpenPosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
address: {
nftMint: nftMintAccount,
tickArrayLower,
tickArrayUpper,
positionNftAccount,
metadataAccount,
personalPosition,
protocolPosition,
},
};
}
static async openPositionFromBaseInstructions({
poolInfo,
poolKeys,
ownerInfo,
tickLower,
tickUpper,
base,
baseAmount,
otherAmountMax,
liquidity,
withMetadata,
getEphemeralSigners,
nft2022,
}: {
poolInfo: SimpleClmmPoolInfo;
poolKeys: {
vault: {
A: string | PublicKey;
B: string | PublicKey;
};
lookupTableAccount?: string;
};
ownerInfo: {
feePayer: PublicKey;
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
tickLower: number;
tickUpper: number;
base: "MintA" | "MintB" | null;
baseAmount: BN;
otherAmountMax: BN;
liquidity: BN;
withMetadata: "create" | "no-create";
getEphemeralSigners?: (k: number) => any;
nft2022?: boolean;
}): Promise<ReturnTypeMakeInstructions<OpenPositionFromBaseExtInfo>> {
const signers: Signer[] = [];
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
let nftMintAccount: PublicKey;
if (getEphemeralSigners) {
nftMintAccount = new PublicKey((await getEphemeralSigners(1))[0]);
} else {
const _k = Keypair.generate();
signers.push(_k);
nftMintAccount = _k.publicKey;
}
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(tickLower, poolInfo.config.tickSpacing);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(tickUpper, poolInfo.config.tickSpacing);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_PROGRAM_ID);
const { publicKey: metadataAccount } = getPdaMetadataKey(nftMintAccount);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, nftMintAccount);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(programId, id, tickLower, tickUpper);
const ins = nft2022
? this.openPositionWithToken22NftInstruction(
programId,
ownerInfo.feePayer,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
liquidity,
!base || base === "MintA" ? baseAmount : otherAmountMax,
!base || base === "MintA" ? otherAmountMax : baseAmount,
withMetadata === "create",
base ? base === "MintA" : base,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
)
: this.openPositionV2Instruction(
programId,
ownerInfo.feePayer,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
metadataAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
BN_ZERO,
base === "MintA" ? baseAmount : otherAmountMax,
base === "MintA" ? otherAmountMax : baseAmount,
withMetadata === "create",
base === "MintA",
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
);
return {
address: {
nftMint: nftMintAccount,
tickArrayLower,
tickArrayUpper,
positionNftAccount,
metadataAccount,
personalPosition,
protocolPosition,
},
instructions: [ins],
signers,
instructionTypes: [InstructionType.ClmmOpenPosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static async openPositionFromLiquidityInstructions({
poolInfo,
poolKeys,
ownerInfo,
tickLower,
tickUpper,
liquidity,
amountMaxA,
amountMaxB,
base,
withMetadata,
getEphemeralSigners,
nft2022,
}: {
poolInfo: SimpleClmmPoolInfo;
poolKeys: ClmmKeys;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
tickLower: number;
tickUpper: number;
liquidity: BN;
amountMaxA: BN;
amountMaxB: BN;
base: "MintA" | "MintB" | null;
withMetadata: "create" | "no-create";
getEphemeralSigners?: (k: number) => any;
nft2022?: boolean;
}): Promise<ReturnTypeMakeInstructions<OpenPositionFromLiquidityExtInfo["address"]>> {
let nftMintAccount: PublicKey;
const signers: Keypair[] = [];
if (getEphemeralSigners) {
nftMintAccount = new PublicKey((await getEphemeralSigners(1))[0]);
} else {
const _k = Keypair.generate();
signers.push(_k);
nftMintAccount = _k.publicKey;
}
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(tickLower, poolInfo.config.tickSpacing);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(tickUpper, poolInfo.config.tickSpacing);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, nftMintAccount, TOKEN_PROGRAM_ID);
const { publicKey: metadataAccount } = getPdaMetadataKey(nftMintAccount);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, nftMintAccount);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(programId, id, tickLower, tickUpper);
const ins = nft2022
? this.openPositionWithToken22NftInstruction(
programId,
ownerInfo.wallet,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolKeys.mintA.address),
new PublicKey(poolKeys.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
liquidity,
amountMaxA,
amountMaxB,
withMetadata === "create",
base ? base === "MintA" : base,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
)
: this.openPositionV2Instruction(
programId,
ownerInfo.wallet,
id,
ownerInfo.wallet,
nftMintAccount,
positionNftAccount,
metadataAccount,
protocolPosition,
tickArrayLower,
tickArrayUpper,
personalPosition,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolKeys.mintA.address),
new PublicKey(poolKeys.mintB.address),
tickLower,
tickUpper,
tickArrayLowerStartIndex,
tickArrayUpperStartIndex,
liquidity,
amountMaxA,
amountMaxB,
withMetadata === "create",
null,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
);
return {
address: {
nftMint: nftMintAccount,
tickArrayLower,
tickArrayUpper,
positionNftAccount,
metadataAccount,
personalPosition,
protocolPosition,
},
instructions: [ins],
signers,
instructionTypes: [InstructionType.ClmmOpenPosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static closePositionInstructions({
poolInfo,
poolKeys,
ownerInfo,
ownerPosition,
nft2022,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerPosition: ReturnType<typeof PersonalPositionLayout.decode>;
ownerInfo: {
wallet: PublicKey;
};
nft2022?: boolean;
}): ReturnTypeMakeInstructions<ClosePositionExtInfo["address"]> {
const programId = new PublicKey(poolInfo.programId);
// const { publicKey: positionNftAccount } = getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_PROGRAM_ID);
const positionNftAccount = nft2022
? getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_2022_PROGRAM_ID).publicKey
: getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_PROGRAM_ID).publicKey;
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
const ins: TransactionInstruction[] = [];
ins.push(
this.closePositionInstruction(
programId,
ownerInfo.wallet,
ownerPosition.nftMint,
positionNftAccount,
personalPosition,
!!nft2022,
new PublicKey(poolInfo.id),
),
);
return {
address: {
positionNftAccount,
personalPosition,
},
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmClosePosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static increasePositionFromLiquidityInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo,
liquidity,
amountMaxA,
amountMaxB,
nft2022,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerPosition: ReturnType<typeof PersonalPositionLayout.decode>;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
liquidity: BN;
amountMaxA: BN;
amountMaxB: BN;
nft2022?: boolean;
}): ReturnTypeMakeInstructions<ManipulateLiquidityExtInfo["address"]> {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickLower,
poolInfo.config.tickSpacing,
);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickUpper,
poolInfo.config.tickSpacing,
);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_PROGRAM_ID);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
programId,
id,
ownerPosition.tickLower,
ownerPosition.tickUpper,
);
const ins = this.increaseLiquidityV2Instruction(
programId,
ownerInfo.wallet,
positionNftAccount,
personalPosition,
id,
protocolPosition,
tickArrayLower,
tickArrayUpper,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
liquidity,
amountMaxA,
amountMaxB,
null,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
);
return {
address: {
tickArrayLower,
tickArrayUpper,
positionNftAccount,
personalPosition,
protocolPosition,
},
signers: [],
instructions: [ins],
instructionTypes: [InstructionType.ClmmIncreasePosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static increasePositionFromBaseInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo,
base,
baseAmount,
otherAmountMax,
nft2022,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerPosition: ClmmPoolPersonalPosition;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
base: "MintA" | "MintB";
baseAmount: BN;
otherAmountMax: BN;
nft2022?: boolean;
}): ReturnTypeMakeInstructions<ManipulateLiquidityExtInfo["address"]> {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickLower,
poolInfo.config.tickSpacing,
);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickUpper,
poolInfo.config.tickSpacing,
);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(programId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(programId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_PROGRAM_ID);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(programId, ownerPosition.nftMint);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
programId,
id,
ownerPosition.tickLower,
ownerPosition.tickUpper,
);
return {
address: {
tickArrayLower,
tickArrayUpper,
positionNftAccount,
personalPosition,
protocolPosition,
},
instructions: [
this.increaseLiquidityV2Instruction(
programId,
ownerInfo.wallet,
positionNftAccount,
personalPosition,
id,
protocolPosition,
tickArrayLower,
tickArrayUpper,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
BN_ZERO,
base === "MintA" ? baseAmount : otherAmountMax,
base === "MintA" ? otherAmountMax : baseAmount,
base === "MintA",
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(programId, id).publicKey
: undefined,
),
],
signers: [],
instructionTypes: [InstructionType.ClmmIncreasePosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static decreaseLiquidityInstructions({
poolInfo,
poolKeys,
ownerPosition,
ownerInfo,
liquidity,
amountMinA,
amountMinB,
programId,
nft2022,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerPosition: ReturnType<typeof PersonalPositionLayout.decode>;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
rewardAccounts: PublicKey[];
};
liquidity: BN;
amountMinA: BN;
amountMinB: BN;
programId?: PublicKey;
nft2022?: boolean;
}): ReturnTypeMakeInstructions<ManipulateLiquidityExtInfo["address"]> {
const [poolProgramId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickLower,
poolInfo.config.tickSpacing,
);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(
ownerPosition.tickUpper,
poolInfo.config.tickSpacing,
);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(poolProgramId, id, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(poolProgramId, id, tickArrayUpperStartIndex);
const { publicKey: positionNftAccount } = nft2022
? getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, TOKEN_2022_PROGRAM_ID)
: getATAAddress(ownerInfo.wallet, ownerPosition.nftMint, programId);
const { publicKey: personalPosition } = getPdaPersonalPositionAddress(poolProgramId, ownerPosition.nftMint);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
poolProgramId,
id,
ownerPosition.tickLower,
ownerPosition.tickUpper,
);
// Match by mint: rewardDefaultInfos and rewardInfos can differ in length/order
const ownerMintToAccount = new Map<string, PublicKey>();
for (let i = 0; i < poolKeys.rewardInfos.length; i++) {
ownerMintToAccount.set(poolKeys.rewardInfos[i].mint.address, ownerInfo.rewardAccounts[i]);
}
const rewardAccounts: {
poolRewardVault: PublicKey;
ownerRewardVault: PublicKey;
rewardMint: PublicKey;
}[] = [];
for (let i = 0; i < poolKeys.rewardInfos.length; i++) {
const mint = poolKeys.rewardInfos[i].mint.address;
const ownerRewardVault = ownerMintToAccount.get(mint);
if (ownerRewardVault) {
rewardAccounts.push({
poolRewardVault: new PublicKey(poolKeys.rewardInfos[i].vault),
ownerRewardVault,
rewardMint: new PublicKey(mint),
});
}
}
const ins: TransactionInstruction[] = [];
const decreaseIns = this.decreaseLiquidityV2Instruction(
poolProgramId,
ownerInfo.wallet,
positionNftAccount,
personalPosition,
id,
protocolPosition,
tickArrayLower,
tickArrayUpper,
ownerInfo.tokenAccountA,
ownerInfo.tokenAccountB,
new PublicKey(poolKeys.vault.A),
new PublicKey(poolKeys.vault.B),
new PublicKey(poolInfo.mintA.address),
new PublicKey(poolInfo.mintB.address),
rewardAccounts,
liquidity,
amountMinA,
amountMinB,
PoolUtil.isOverflowDefaultTickarrayBitmap({
tickSpacing: poolInfo.config.tickSpacing,
tickIndexs: [tickArrayLowerStartIndex, tickArrayUpperStartIndex],
})
? getPdaExBitmapAccount(poolProgramId, id).publicKey
: undefined,
);
ins.push(decreaseIns);
return {
address: {
tickArrayLower,
tickArrayUpper,
positionNftAccount,
personalPosition,
protocolPosition,
},
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmDecreasePosition],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static makeSwapBaseInInstructions({
poolInfo,
poolKeys,
observationId,
ownerInfo,
inputMint,
amountIn,
amountOutMin,
sqrtPriceLimitX64,
remainingAccounts,
}: {
poolInfo: SimpleClmmPoolInfo;
poolKeys: Pick<ClmmKeys, "vault" | "lookupTableAccount">;
observationId: PublicKey;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
inputMint: PublicKey;
amountIn: BN;
amountOutMin: BN;
sqrtPriceLimitX64: BN;
remainingAccounts: PublicKey[];
}): ReturnTypeMakeInstructions {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const [mintAVault, mintBVault] = [new PublicKey(poolKeys.vault.A), new PublicKey(poolKeys.vault.B)];
const [mintA, mintB] = [new PublicKey(poolInfo.mintA.address), new PublicKey(poolInfo.mintB.address)];
const isInputMintA = poolInfo.mintA.address.toString() === inputMint.toString();
const ins = [
this.swapV2Instruction(
programId,
ownerInfo.wallet,
id,
new PublicKey(poolInfo.config.id),
isInputMintA ? ownerInfo.tokenAccountA : ownerInfo.tokenAccountB,
isInputMintA ? ownerInfo.tokenAccountB : ownerInfo.tokenAccountA,
isInputMintA ? mintAVault : mintBVault,
isInputMintA ? mintBVault : mintAVault,
isInputMintA ? mintA : mintB,
isInputMintA ? mintB : mintA,
remainingAccounts,
observationId,
amountIn,
amountOutMin,
sqrtPriceLimitX64,
true,
getPdaExBitmapAccount(programId, id).publicKey,
),
];
return {
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmSwapBaseIn],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
address: {},
};
}
static makeSwapBaseOutInstructions({
poolInfo,
poolKeys,
observationId,
ownerInfo,
outputMint,
amountOut,
amountInMax,
sqrtPriceLimitX64,
remainingAccounts,
}: {
poolInfo: SimpleClmmPoolInfo;
poolKeys: Pick<ClmmKeys, "vault" | "lookupTableAccount">;
observationId: PublicKey;
ownerInfo: {
wallet: PublicKey;
tokenAccountA: PublicKey;
tokenAccountB: PublicKey;
};
outputMint: PublicKey;
amountOut: BN;
amountInMax: BN;
sqrtPriceLimitX64: BN;
remainingAccounts: PublicKey[];
}): ReturnTypeMakeInstructions {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const [mintAVault, mintBVault] = [new PublicKey(poolKeys.vault.A), new PublicKey(poolKeys.vault.B)];
const [mintA, mintB] = [new PublicKey(poolInfo.mintA.address), new PublicKey(poolInfo.mintB.address)];
const isInputMintA = poolInfo.mintB.address.toString() === outputMint.toBase58();
const ins = [
this.swapV2Instruction(
programId,
ownerInfo.wallet,
id,
new PublicKey(poolInfo.config.id),
isInputMintA ? ownerInfo.tokenAccountA : ownerInfo.tokenAccountB,
isInputMintA ? ownerInfo.tokenAccountB : ownerInfo.tokenAccountA,
isInputMintA ? mintAVault : mintBVault,
isInputMintA ? mintBVault : mintAVault,
isInputMintA ? mintA : mintB,
isInputMintA ? mintB : mintA,
remainingAccounts,
observationId,
amountOut,
amountInMax,
sqrtPriceLimitX64,
false,
getPdaExBitmapAccount(programId, id).publicKey,
),
];
return {
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmSwapBaseOut],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
address: {},
};
}
static initRewardInstructions({
poolInfo,
poolKeys,
ownerInfo,
rewardInfo,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerInfo: {
wallet: PublicKey;
tokenAccount: PublicKey;
};
rewardInfo: {
programId: PublicKey;
mint: PublicKey;
openTime: number;
endTime: number;
emissionsPerSecondX64: BN;
};
}): ReturnTypeMakeInstructions<InitRewardExtInfo["address"]> {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
const poolRewardVault = getPdaPoolRewardVaultId(programId, id, rewardInfo.mint).publicKey;
const operationId = getPdaOperationAccount(programId).publicKey;
const ins = [
this.initializeRewardInstruction(
programId,
ownerInfo.wallet,
id,
operationId,
new PublicKey(poolInfo.config.id),
ownerInfo.tokenAccount,
rewardInfo.programId,
rewardInfo.mint,
poolRewardVault,
new BN(rewardInfo.openTime),
new BN(rewardInfo.endTime),
rewardInfo.emissionsPerSecondX64,
),
];
return {
address: { poolRewardVault, operationId },
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmInitReward],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static setRewardInstructions({
poolInfo,
poolKeys,
ownerInfo,
rewardInfo,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerInfo: {
wallet: PublicKey;
tokenAccount: PublicKey;
};
rewardInfo: {
mint: PublicKey;
openTime: number;
endTime: number;
emissionsPerSecondX64: BN;
};
}): ReturnTypeMakeInstructions {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
let rewardIndex: number | undefined;
let rewardVault: PublicKey | undefined;
let rewardMint: PublicKey | undefined;
for (let index = 0; index < poolInfo.rewardDefaultInfos.length; index++)
if (poolInfo.rewardDefaultInfos[index].mint.address === rewardInfo.mint.toString()) {
rewardIndex = index;
rewardVault = new PublicKey(poolKeys.rewardInfos[index].vault);
rewardMint = new PublicKey(poolKeys.rewardInfos[index].mint.address);
}
if (rewardIndex === undefined || rewardVault === undefined)
logger.logWithError("reward mint check error", "no reward mint", poolInfo.rewardDefaultInfos);
const operationId = getPdaOperationAccount(programId).publicKey;
const ins = [
this.setRewardParamsInstruction(
programId,
ownerInfo.wallet,
id,
operationId,
new PublicKey(poolInfo.config.id),
ownerInfo.tokenAccount,
rewardVault!,
rewardMint!,
rewardIndex!,
new BN(rewardInfo.openTime),
new BN(rewardInfo.endTime),
rewardInfo.emissionsPerSecondX64,
),
];
return {
address: { rewardVault: rewardVault!, operationId },
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmSetReward],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static collectRewardInstructions({
poolInfo,
poolKeys,
ownerInfo,
rewardMint,
}: {
poolInfo: ApiV3PoolInfoConcentratedItem;
poolKeys: ClmmKeys;
ownerInfo: {
wallet: PublicKey;
tokenAccount: PublicKey;
};
rewardMint: PublicKey;
}): ReturnTypeMakeInstructions {
const [programId, id] = [new PublicKey(poolInfo.programId), new PublicKey(poolInfo.id)];
let rewardIndex: number | undefined;
let rewardVault: PublicKey | undefined;
for (let index = 0; index < poolInfo.rewardDefaultInfos.length; index++)
if (poolInfo.rewardDefaultInfos[index].mint.address === rewardMint.toString()) {
rewardIndex = index;
rewardVault = new PublicKey(poolKeys.rewardInfos[index].vault);
}
if (rewardIndex === undefined || rewardVault === undefined)
logger.logWithError("reward mint check error", "no reward mint", poolInfo.rewardDefaultInfos);
const ins = [
this.collectRemainingRewardsInstruction(
programId,
ownerInfo.wallet,
id,
ownerInfo.tokenAccount,
rewardVault!,
rewardMint,
rewardIndex!,
),
];
return {
address: { rewardVault: rewardVault! },
signers: [],
instructions: ins,
instructionTypes: [InstructionType.ClmmCollectReward],
lookupTableAddress: poolKeys.lookupTableAccount ? [poolKeys.lookupTableAccount] : [],
};
}
static async makeLockPositions({
programId,
authProgramId,
poolProgramId,
payer,
wallet,
nftMint,
nft2022,
getEphemeralSigners,
}: {
programId: PublicKey;
authProgramId: PublicKey;
poolProgramId: PublicKey;
wallet: PublicKey;
payer: PublicKey;
nftMint: PublicKey;
nft2022?: boolean;
getEphemeralSigners?: (k: number) => any;
}): Promise<ReturnTypeMakeInstructions<ClmmLockAddress>> {
const signers: Signer[] = [];
let lockNftMint: PublicKey;
if (getEphemeralSigners) {
lockNftMint = new PublicKey((await getEphemeralSigners(1))[0]);
} else {
const _k = Keypair.generate();
signers.push(_k);
lockNftMint = _k.publicKey;
}
const positionNftAccount = nft2022
? getATAAddress(wallet, nftMint, TOKEN_2022_PROGRAM_ID).publicKey
: getATAAddress(wallet, nftMint, TOKEN_PROGRAM_ID).publicKey;
const { publicKey: positionId } = getPdaPersonalPositionAddress(poolProgramId, nftMint);
const lockPositionId = getPdaLockClPositionIdV2(programId, lockNftMint).publicKey;
const lockNftAccount = getATAAddress(wallet, lockNftMint, TOKEN_PROGRAM_ID).publicKey;
const metadataAccount = getPdaMetadataKey(lockNftMint).publicKey;
const ins = ClmmInstrument.lockPositionInstructionV2({
programId,
auth: authProgramId,
payer,
positionOwner: wallet,
lockOwner: wallet,
positionNftAccount,
positionId,
lockPositionId,
lockNftMint,
lockNftAccount,
metadataAccount,
withMetadata: true,
nft2022,
positionNftMint: nftMint,
authPositionNftAccount: getATAAddress(authProgramId, nftMint, nft2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID)
.publicKey,
positionNftProgram: nft2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID,
});
return {
address: {
positionId,
lockPositionId,
lockNftAccount,
lockNftMint,
positionNftAccount,
metadataAccount,
},
instructions: [ins],
signers,
instructionTypes: [InstructionType.ClmmLockPosition],
lookupTableAddress: [],
};
}
static lockPositionInstructionV2({
programId,
auth,
payer,
positionOwner,
lockOwner,
positionNftAccount,
positionId,
positionNftMint,
authPositionNftAccount,
positionNftProgram,
lockPositionId,
lockNftMint,
lockNftAccount,
metadataAccount,
withMetadata,
}: {
programId: PublicKey;
auth: PublicKey;
payer: PublicKey;
positionOwner: PublicKey;
lockOwner: PublicKey;
positionNftAccount: PublicKey;
positionId: PublicKey;
positionNftMint: PublicKey;
authPositionNftAccount: PublicKey;
positionNftProgram: PublicKey;
lockPositionId: PublicKey;
lockNftMint: PublicKey;
lockNftAccount: PublicKey;
metadataAccount: PublicKey;
withMetadata: boolean;
nft2022?: boolean;
}): TransactionInstruction {
const keys = [
{ pubkey: auth, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: true },
{ pubkey: positionOwner, isSigner: true, isWritable: true },
{ pubkey: lockOwner, isSigner: false, isWritable: false },
{ pubkey: positionNftAccount, isSigner: false, isWritable: true },
{ pubkey: positionId, isSigner: false, isWritable: false },
{ pubkey: positionNftMint, isSigner: false, isWritable: true },
{ pubkey: authPositionNftAccount, isSigner: false, isWritable: true },
{ pubkey: lockPositionId, isSigner: false, isWritable: true },
{ pubkey: lockNftMint, isSigner: true, isWritable: true },
{ pubkey: lockNftAccount, isSigner: false, isWritable: true },
{ pubkey: metadataAccount, isSigner: false, isWritable: true },
{ pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: positionNftProgram, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
];
const dataLayout = struct([bool("withMetadata")]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
withMetadata,
},
data,
);
const aData = Buffer.from([...lockInsDataBuf, ...data]);
return new TransactionInstruction({
keys,
programId,
data: aData,
});
}
static lockPositionInstruction({
programId,
authProgramId,
poolProgramId,
owner,
positionNft,
}: {
programId: PublicKey;
authProgramId: PublicKey;
poolProgramId: PublicKey;
owner: PublicKey;
positionNft: PublicKey;
}): TransactionInstruction {
const { publicKey: nftAccount } = getATAAddress(owner, positionNft, TOKEN_PROGRAM_ID);
const { publicKey: positionId } = getPdaPersonalPositionAddress(poolProgramId, positionNft);
const keys = [
{ pubkey: authProgramId, isSigner: false, isWritable: false },
{ pubkey: owner, isSigner: true, isWritable: false },
{ pubkey: nftAccount, isSigner: false, isWritable: true },
{ pubkey: positionId, isSigner: false, isWritable: false },
{ pubkey: getPdaLockPositionId(programId, positionId).publicKey, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
];
return new TransactionInstruction({
keys,
programId,
data: Buffer.from(lockInsDataBuf),
});
}
static harvestLockPositionInstruction(props: {
poolKeys: ClmmKeys;
programId: PublicKey;
authProgramId: PublicKey;
ownerPosition: ReturnType<typeof PersonalPositionLayout.decode>;
owner: PublicKey;
ownerRewardAccounts: PublicKey[];
userVaultA: PublicKey;
userVaultB: PublicKey;
}): TransactionInstruction {
const [poolProgramId, poolId] = [new PublicKey(props.poolKeys.programId), new PublicKey(props.poolKeys.id)];
const tickArrayLowerStartIndex = TickArrayUtil.getTickArrayStartIndex(
props.ownerPosition.tickLower,
props.poolKeys.config.tickSpacing,
);
const tickArrayUpperStartIndex = TickArrayUtil.getTickArrayStartIndex(
props.ownerPosition.tickUpper,
props.poolKeys.config.tickSpacing,
);
const { publicKey: tickArrayLower } = getPdaTickArrayAddress(poolProgramId, poolId, tickArrayLowerStartIndex);
const { publicKey: tickArrayUpper } = getPdaTickArrayAddress(poolProgramId, poolId, tickArrayUpperStartIndex);
const { publicKey: nftAccount } = getATAAddress(props.owner, props.ownerPosition.nftMint, TOKEN_PROGRAM_ID);
const { publicKey: positionId } = getPdaPersonalPositionAddress(poolProgramId, props.ownerPosition.nftMint);
const { publicKey: protocolPosition } = getPdaProtocolPositionAddress(
poolProgramId,
poolId,
props.ownerPosition.tickLower,
props.ownerPosition.tickUpper,
);
const rewardAccounts: {
poolRewardVault: PublicKey;
ownerRewardVault: PublicKey;
rewardMint: PublicKey;
}[] = [];
for (let i = 0; i < props.poolKeys.rewardInfos.length; i++) {
rewardAccounts.push({
poolRewardVault: new PublicKey(props.poolKeys.rewardInfos[i].vault),
ownerRewardVault: props.ownerRewardAccounts[i],
rewardMint: new PublicKey(props.poolKeys.rewardInfos[i].mint.address),
});
}
const remainingAccounts = [
...rewardAccounts
.map((i) => [
{ pubkey: i.poolRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.ownerRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.rewardMint, isSigner: false, isWritable: false },
])
.flat(),
];
const keys = [
{ pubkey: props.authProgramId, isSigner: false, isWritable: false },
{ pubkey: getPdaLockPositionId(props.programId, positionId).publicKey, isSigner: false, isWritable: false },
{ pubkey: poolProgramId, isSigner: false, isWritable: false },
{ pubkey: props.owner, isSigner: true, isWritable: false },
{ pubkey: nftAccount, isSigner: false, isWritable: true },
{ pubkey: positionId, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: true },
{ pubkey: new PublicKey(props.poolKeys.vault.A), isSigner: false, isWritable: true },
{ pubkey: new PublicKey(props.poolKeys.vault.B), isSigner: false, isWritable: true },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: props.userVaultA, isSigner: false, isWritable: true },
{ pubkey: props.userVaultB, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: new PublicKey(props.poolKeys.mintA.address), isSigner: false, isWritable: false },
{ pubkey: new PublicKey(props.poolKeys.mintB.address), isSigner: false, isWritable: false },
...remainingAccounts,
];
return new TransactionInstruction({
keys,
programId: props.programId,
data: Buffer.from(lockHarvestInsDataBuf),
});
}
static harvestLockPositionInstructionV2({
programId,
auth,
lockPositionId,
clmmProgram,
lockOwner,
lockNftMint,
lockNftAccount,
positionNftAccount,
positionId,
poolId,
protocolPosition,
vaultA,
vaultB,
tickArrayLower,
tickArrayUpper,
userVaultA,
userVaultB,
mintA,
mintB,
rewardAccounts,
exTickArrayBitmap,
}: {
programId: PublicKey;
auth: PublicKey;
lockPositionId: PublicKey;
clmmProgram: PublicKey;
lockOwner: PublicKey;
lockNftMint: PublicKey;
lockNftAccount: PublicKey;
positionNftAccount: PublicKey;
positionId: PublicKey;
poolId: PublicKey;
protocolPosition: PublicKey;
vaultA: PublicKey;
vaultB: PublicKey;
tickArrayLower: PublicKey;
tickArrayUpper: PublicKey;
userVaultA: PublicKey;
userVaultB: PublicKey;
mintA: PublicKey;
mintB: PublicKey;
rewardAccounts: {
poolRewardVault: PublicKey;
ownerRewardVault: PublicKey;
rewardMint: PublicKey;
}[];
exTickArrayBitmap?: PublicKey;
}): TransactionInstruction {
const remainingAccounts = [
...(exTickArrayBitmap ? [{ pubkey: exTickArrayBitmap, isSigner: false, isWritable: true }] : []),
...rewardAccounts
.map((i) => [
{ pubkey: i.poolRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.ownerRewardVault, isSigner: false, isWritable: true },
{ pubkey: i.rewardMint, isSigner: false, isWritable: false },
])
.flat(),
];
const keys = [
{ pubkey: auth, isSigner: false, isWritable: false },
{ pubkey: lockOwner, isSigner: true, isWritable: false },
// { pubkey: lockNftMint, isSigner: false, isWritable: false },
{ pubkey: lockNftAccount, isSigner: false, isWritable: true },
{ pubkey: lockPositionId, isSigner: false, isWritable: false },
{ pubkey: clmmProgram, isSigner: false, isWritable: false },
{ pubkey: positionNftAccount, isSigner: false, isWritable: true },
{ pubkey: positionId, isSigner: false, isWritable: true },
{ pubkey: poolId, isSigner: false, isWritable: true },
{ pubkey: protocolPosition, isSigner: false, isWritable: true },
{ pubkey: vaultA, isSigner: false, isWritable: true },
{ pubkey: vaultB, isSigner: false, isWritable: true },
{ pubkey: tickArrayLower, isSigner: false, isWritable: true },
{ pubkey: tickArrayUpper, isSigner: false, isWritable: true },
{ pubkey: userVaultA, isSigner: false, isWritable: true },
{ pubkey: userVaultB, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: TOKEN_2022_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: MEMO_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: mintA, isSigner: false, isWritable: false },
{ pubkey: mintB, isSigner: false, isWritable: false },
...remainingAccounts,
];
return new TransactionInstruction({
keys,
programId,
data: Buffer.from(lockHarvestInsDataBuf),
});
}
}