mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-14 07:26:46 +00:00
Merge pull request #23 from calintje/main
Add single-sided pool creation on Orca
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@bonfida/spl-name-service": "^3.0.7",
|
||||
"@coral-xyz/anchor": "0.29",
|
||||
"@langchain/core": "^0.3.18",
|
||||
"@langchain/groq": "^0.1.2",
|
||||
"@langchain/langgraph": "^0.2.27",
|
||||
@@ -26,9 +27,12 @@
|
||||
"@metaplex-foundation/umi": "^0.9.2",
|
||||
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
|
||||
"@metaplex-foundation/umi-web3js-adapters": "^0.9.2",
|
||||
"@orca-so/common-sdk": "0.6.4",
|
||||
"@orca-so/whirlpools-sdk": "^0.13.12",
|
||||
"@solana/spl-token": "^0.4.9",
|
||||
"@solana/web3.js": "^1.95.4",
|
||||
"bs58": "^6.0.0",
|
||||
"decimal.js": "^10.4.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"form-data": "^4.0.1",
|
||||
"langchain": "^0.3.6",
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -53,6 +53,9 @@ importers:
|
||||
bs58:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
decimal.js:
|
||||
specifier: ^10.4.3
|
||||
version: 10.4.3
|
||||
dotenv:
|
||||
specifier: ^16.4.5
|
||||
version: 16.4.7
|
||||
@@ -658,6 +661,9 @@ packages:
|
||||
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
decimal.js@10.4.3:
|
||||
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
|
||||
|
||||
delay@5.0.0:
|
||||
resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1965,6 +1971,8 @@ snapshots:
|
||||
|
||||
decamelize@1.2.0: {}
|
||||
|
||||
decimal.js@10.4.3: {}
|
||||
|
||||
delay@5.0.0: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
@@ -18,9 +18,13 @@ import {
|
||||
getTokenDataByTicker,
|
||||
stakeWithJup,
|
||||
sendCompressedAirdrop,
|
||||
createOrcaSingleSidedWhirlpool,
|
||||
FEE_TIERS
|
||||
} from "../tools";
|
||||
import { CollectionOptions, PumpFunTokenOptions } from "../types";
|
||||
import { DEFAULT_OPTIONS } from "../constants";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
import Decimal from "decimal.js";
|
||||
|
||||
/**
|
||||
* Main class for interacting with Solana blockchain
|
||||
@@ -159,4 +163,23 @@ export class SolanaAgentKit {
|
||||
shouldLog
|
||||
);
|
||||
}
|
||||
|
||||
async createOrcaSingleSidedWhirlpool(
|
||||
depositTokenAmount: BN,
|
||||
depositTokenMint: PublicKey,
|
||||
otherTokenMint: PublicKey,
|
||||
initialPrice: Decimal,
|
||||
maxPrice: Decimal,
|
||||
feeTier: keyof typeof FEE_TIERS,
|
||||
) {
|
||||
return createOrcaSingleSidedWhirlpool(
|
||||
this,
|
||||
depositTokenAmount,
|
||||
depositTokenMint,
|
||||
otherTokenMint,
|
||||
initialPrice,
|
||||
maxPrice,
|
||||
feeTier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { PublicKey } from "@solana/web3.js";
|
||||
import { toJSON } from "../utils/toJSON";
|
||||
import { create_image } from "../tools/create_image";
|
||||
import { fetchPrice } from "../tools/fetch_price";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
import Decimal from "decimal.js";
|
||||
import { FEE_TIERS } from "../tools";
|
||||
|
||||
export class SolanaBalanceTool extends Tool {
|
||||
name = "solana_balance";
|
||||
@@ -746,6 +749,61 @@ export class SolanaCompressedAirdropTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
|
||||
name = "create_orca_single_sided_whirlpool";
|
||||
description = `Create a single-sided Whirlpool with liquidity.
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
- depositTokenAmount: number, eg: 1000000000 (required, in units of deposit token including decimals)
|
||||
- depositTokenMint: string, eg: "DepositTokenMintAddress" (required, mint address of deposit token)
|
||||
- otherTokenMint: string, eg: "OtherTokenMintAddress" (required, mint address of other token)
|
||||
- initialPrice: number, eg: 0.001 (required, initial price of deposit token in terms of other token)
|
||||
- maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added)
|
||||
- feeTier: number, eg: 0.30 (required, fee tier for the pool)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const inputFormat = JSON.parse(input);
|
||||
const depositTokenAmount = new BN(inputFormat.depositTokenAmount);
|
||||
const depositTokenMint = new PublicKey(inputFormat.depositTokenMint);
|
||||
const otherTokenMint = new PublicKey(inputFormat.otherTokenMint);
|
||||
const initialPrice = new Decimal(inputFormat.initialPrice);
|
||||
const maxPrice = new Decimal(inputFormat.maxPrice);
|
||||
const feeTier = inputFormat.feeTier;
|
||||
|
||||
if (!feeTier || !(feeTier in FEE_TIERS)) {
|
||||
throw new Error(`Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`);
|
||||
}
|
||||
|
||||
const txId = await this.solanaKit.createOrcaSingleSidedWhirlpool(
|
||||
depositTokenAmount,
|
||||
depositTokenMint,
|
||||
otherTokenMint,
|
||||
initialPrice,
|
||||
maxPrice,
|
||||
feeTier,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Single-sided Whirlpool created successfully",
|
||||
transaction: txId,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
return [
|
||||
new SolanaBalanceTool(solanaKit),
|
||||
|
||||
383
src/tools/create_orca_single_sided_whirlpool.ts
Normal file
383
src/tools/create_orca_single_sided_whirlpool.ts
Normal file
@@ -0,0 +1,383 @@
|
||||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { BN, Wallet } from "@coral-xyz/anchor";
|
||||
import { Decimal } from "decimal.js";
|
||||
import {
|
||||
PDAUtil,
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
ORCA_WHIRLPOOLS_CONFIG,
|
||||
WhirlpoolContext,
|
||||
TickUtil,
|
||||
PriceMath,
|
||||
PoolUtil,
|
||||
TokenExtensionContextForPool,
|
||||
NO_TOKEN_EXTENSION_CONTEXT,
|
||||
TokenExtensionUtil,
|
||||
WhirlpoolIx,
|
||||
IncreaseLiquidityQuoteParam,
|
||||
increaseLiquidityQuoteByInputTokenWithParams,
|
||||
} from "@orca-so/whirlpools-sdk";
|
||||
import {
|
||||
Percentage,
|
||||
resolveOrCreateATAs,
|
||||
TransactionBuilder,
|
||||
} from "@orca-so/common-sdk";
|
||||
import {
|
||||
increaseLiquidityIx,
|
||||
increaseLiquidityV2Ix,
|
||||
initTickArrayIx,
|
||||
openPositionWithTokenExtensionsIx,
|
||||
} from "@orca-so/whirlpools-sdk/dist/instructions";
|
||||
import {
|
||||
getAssociatedTokenAddressSync,
|
||||
TOKEN_2022_PROGRAM_ID,
|
||||
} from "@solana/spl-token";
|
||||
|
||||
/**
|
||||
* Maps fee tier percentages to their corresponding tick spacing values in the Orca Whirlpool protocol.
|
||||
*
|
||||
* @remarks
|
||||
* Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects
|
||||
* the granularity of price ranges for liquidity positions.
|
||||
*
|
||||
* For more details, refer to:
|
||||
* - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees)
|
||||
* - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters)
|
||||
*
|
||||
* @example
|
||||
* const tickSpacing = FEE_TIERS[0.30]; // Returns 64
|
||||
*/
|
||||
export const FEE_TIERS = {
|
||||
0.01: 1,
|
||||
0.02: 2,
|
||||
0.04: 4,
|
||||
0.05: 8,
|
||||
0.16: 16,
|
||||
0.30: 64,
|
||||
0.65: 96,
|
||||
1.00: 128,
|
||||
2.00: 256,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* # Creates a single-sided Whirlpool.
|
||||
*
|
||||
* This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token.
|
||||
*
|
||||
* ## Example Usage:
|
||||
* You created a new token called SHARK, and you want to set the initial price to 0.001 USDC.
|
||||
* You set `depositTokenMint` to SHARK's mint address and `otherTokenMint` to USDC's mint address.
|
||||
* You can minimize price impact for buyers in a few ways:
|
||||
* 1. Increase the amount of tokens you deposit
|
||||
* 2. Set the initial price very low
|
||||
* 3. Set the maximum price closer to the initial price
|
||||
*
|
||||
* ### Note for experts:
|
||||
* The Wrhirlpool program initializes the Whirlpool with the in a specific order. This might not be
|
||||
* the order you expect, so the function checks the order and adjusts the inverts the prices. This means that
|
||||
* on-chain the Whirlpool might be configured as USDC/SHARK instead of SHARK/USDC, and the on-chain price will
|
||||
* be 1/`initialPrice`. This will not affect the price of the token as you intended it to be.
|
||||
*
|
||||
* @param agent - The `SolanaAgentKit` instance representing the wallet and connection details.
|
||||
* @param depositTokenAmount - The amount of the deposit token (including the decimals) to contribute to the pool.
|
||||
* @param depositTokenMint - The mint address of the token being deposited into the pool, eg. SHARK.
|
||||
* @param otherTokenMint - The mint address of the other token in the pool, eg. USDC.
|
||||
* @param initialPrice - The initial price of the deposit token in terms of the other token.
|
||||
* @param maxPrice - The maximum price at which liquidity is added.
|
||||
* @param feeTier - The fee tier percentage for the pool, determining tick spacing and fee collection rates.
|
||||
*
|
||||
* @returns A promise that resolves to a transaction ID (`string`) of the transaction creating the pool.
|
||||
*
|
||||
* @throws Will throw an error if:
|
||||
* - Mint accounts for the tokens cannot be fetched.
|
||||
* - Prices are out of bounds.
|
||||
*
|
||||
* @remarks
|
||||
* This function is designed for single-sided deposits where users only contribute one type of token,
|
||||
* and the function manages mint order and necessary calculations.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { SolanaAgentKit } from "your-sdk";
|
||||
* import { PublicKey } from "@solana/web3.js";
|
||||
* import { BN } from "@coral-xyz/anchor";
|
||||
* import Decimal from "decimal.js";
|
||||
*
|
||||
* const agent = new SolanaAgentKit(wallet, connection);
|
||||
* const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals
|
||||
* const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS");
|
||||
* const otherTokenMint = new PublicKey("OTHER_TOKEN_ADDRESS");
|
||||
* const initialPrice = new Decimal(0.001);
|
||||
* const maxPrice = new Decimal(5.0);
|
||||
* const feeTier = 0.30;
|
||||
*
|
||||
* const txId = await createOrcaSingleSidedWhirlpool(
|
||||
* agent,
|
||||
* depositAmount,
|
||||
* depositTokenMint,
|
||||
* otherTokenMint,
|
||||
* initialPrice,
|
||||
* maxPrice,
|
||||
* feeTier,
|
||||
* );
|
||||
* console.log(`Single sided whirlpool created in transaction: ${txId}`);
|
||||
* ```
|
||||
*/
|
||||
export async function createOrcaSingleSidedWhirlpool(
|
||||
agent: SolanaAgentKit,
|
||||
depositTokenAmount: BN,
|
||||
depositTokenMint: PublicKey,
|
||||
otherTokenMint: PublicKey,
|
||||
initialPrice: Decimal,
|
||||
maxPrice: Decimal,
|
||||
feeTier: keyof typeof FEE_TIERS,
|
||||
): Promise<string> {
|
||||
const wallet = new Wallet(agent.wallet);
|
||||
const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID);
|
||||
const fetcher = ctx.fetcher;
|
||||
|
||||
const correctTokenOrder = PoolUtil.orderMints(otherTokenMint, depositTokenMint).map(
|
||||
(addr) => addr.toString(),
|
||||
);
|
||||
const isCorrectMintOrder = correctTokenOrder[0] === depositTokenMint.toString();
|
||||
let mintA, mintB;
|
||||
if (isCorrectMintOrder) {
|
||||
[mintA, mintB] = [depositTokenMint, otherTokenMint];
|
||||
} else {
|
||||
[mintA, mintB] = [otherTokenMint, depositTokenMint];
|
||||
initialPrice = new Decimal(1 / initialPrice.toNumber());
|
||||
maxPrice = new Decimal(1 / maxPrice.toNumber());
|
||||
}
|
||||
const mintAAccount = await fetcher.getMintInfo(mintA);
|
||||
const mintBAccount = await fetcher.getMintInfo(mintB);
|
||||
if (mintAAccount === null || mintBAccount === null) throw Error('Mint account not found');
|
||||
const tickSpacing = FEE_TIERS[feeTier];
|
||||
const tickIndex = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
const initialTick = TickUtil.getInitializableTickIndex(tickIndex, tickSpacing);
|
||||
|
||||
const tokenExtensionCtx: TokenExtensionContextForPool = {
|
||||
...NO_TOKEN_EXTENSION_CONTEXT,
|
||||
tokenMintWithProgramA: mintAAccount,
|
||||
tokenMintWithProgramB: mintBAccount,
|
||||
};
|
||||
const feeTierKey = PDAUtil.getFeeTier(
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
ORCA_WHIRLPOOLS_CONFIG,
|
||||
tickSpacing,
|
||||
).publicKey;
|
||||
const initSqrtPrice = PriceMath.tickIndexToSqrtPriceX64(initialTick);
|
||||
const tokenVaultAKeypair = Keypair.generate();
|
||||
const tokenVaultBKeypair = Keypair.generate();
|
||||
const whirlpoolPda = PDAUtil.getWhirlpool(
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
ORCA_WHIRLPOOLS_CONFIG,
|
||||
mintA,
|
||||
mintB,
|
||||
FEE_TIERS[feeTier],
|
||||
);
|
||||
const tokenBadgeA = PDAUtil.getTokenBadge(
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
ORCA_WHIRLPOOLS_CONFIG,
|
||||
mintA,
|
||||
).publicKey;
|
||||
const tokenBadgeB = PDAUtil.getTokenBadge(
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
ORCA_WHIRLPOOLS_CONFIG,
|
||||
mintB,
|
||||
).publicKey;
|
||||
const baseParamsPool = {
|
||||
initSqrtPrice,
|
||||
whirlpoolsConfig: ORCA_WHIRLPOOLS_CONFIG,
|
||||
whirlpoolPda,
|
||||
tokenMintA: mintA,
|
||||
tokenMintB: mintB,
|
||||
tokenVaultAKeypair,
|
||||
tokenVaultBKeypair,
|
||||
feeTierKey,
|
||||
tickSpacing: tickSpacing,
|
||||
funder: wallet.publicKey
|
||||
};
|
||||
const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
|
||||
? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool)
|
||||
: WhirlpoolIx.initializePoolV2Ix(ctx.program, {
|
||||
...baseParamsPool,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
tokenBadgeA,
|
||||
tokenBadgeB,
|
||||
});
|
||||
const initialTickArrayStartTick = TickUtil.getStartTickIndex(
|
||||
initialTick,
|
||||
tickSpacing,
|
||||
);
|
||||
const initialTickArrayPda = PDAUtil.getTickArray(
|
||||
ctx.program.programId,
|
||||
whirlpoolPda.publicKey,
|
||||
initialTickArrayStartTick,
|
||||
);
|
||||
|
||||
const txBuilder = new TransactionBuilder(
|
||||
ctx.provider.connection,
|
||||
ctx.provider.wallet,
|
||||
ctx.txBuilderOpts,
|
||||
);
|
||||
txBuilder.addInstruction(initPoolIx);
|
||||
txBuilder.addInstruction(
|
||||
initTickArrayIx(ctx.program, {
|
||||
startTick: initialTickArrayStartTick,
|
||||
tickArrayPda: initialTickArrayPda,
|
||||
whirlpool: whirlpoolPda.publicKey,
|
||||
funder: wallet.publicKey,
|
||||
}),
|
||||
);
|
||||
|
||||
let tickLowerIndex, tickUpperIndex;
|
||||
if (isCorrectMintOrder) {
|
||||
tickLowerIndex = initialTick;
|
||||
tickUpperIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
} else {
|
||||
tickLowerIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
tickUpperIndex = initialTick;
|
||||
}
|
||||
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(tickLowerIndex, tickSpacing);
|
||||
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(tickUpperIndex, tickSpacing);
|
||||
if (!TickUtil.checkTickInBounds(tickLowerInitializableIndex) || !TickUtil.checkTickInBounds(tickUpperInitializableIndex)) throw Error('Prices out of bounds');
|
||||
const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = {
|
||||
inputTokenAmount: new BN(depositTokenAmount),
|
||||
inputTokenMint: depositTokenMint,
|
||||
tokenMintA: mintA,
|
||||
tokenMintB: mintB,
|
||||
tickCurrentIndex: initialTick,
|
||||
sqrtPrice: initSqrtPrice,
|
||||
tickLowerIndex: tickLowerInitializableIndex,
|
||||
tickUpperIndex: tickUpperInitializableIndex,
|
||||
tokenExtensionCtx: tokenExtensionCtx,
|
||||
slippageTolerance: Percentage.fromFraction(0, 100)
|
||||
}
|
||||
const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams(
|
||||
increasLiquidityQuoteParam
|
||||
)
|
||||
const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput;
|
||||
|
||||
const positionMintKeypair = Keypair.generate();
|
||||
const positionMintPubkey = positionMintKeypair.publicKey;
|
||||
const positionPda = PDAUtil.getPosition(
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
positionMintPubkey,
|
||||
);
|
||||
const positionTokenAccountAddress = getAssociatedTokenAddressSync(
|
||||
positionMintPubkey,
|
||||
wallet.publicKey,
|
||||
ctx.accountResolverOpts.allowPDAOwnerAddress,
|
||||
TOKEN_2022_PROGRAM_ID,
|
||||
);
|
||||
const params = {
|
||||
funder: wallet.publicKey,
|
||||
owner: wallet.publicKey,
|
||||
positionPda,
|
||||
positionTokenAccount: positionTokenAccountAddress,
|
||||
whirlpool: whirlpoolPda.publicKey,
|
||||
tickLowerIndex: tickLowerInitializableIndex,
|
||||
tickUpperIndex: tickUpperInitializableIndex,
|
||||
};
|
||||
const positionIx = openPositionWithTokenExtensionsIx(ctx.program, {
|
||||
...params,
|
||||
positionMint: positionMintPubkey,
|
||||
withTokenMetadataExtension: true,
|
||||
})
|
||||
|
||||
txBuilder.addInstruction(positionIx);
|
||||
txBuilder.addSigner(positionMintKeypair);
|
||||
|
||||
const [ataA, ataB] = await resolveOrCreateATAs(
|
||||
ctx.connection,
|
||||
wallet.publicKey,
|
||||
[
|
||||
{ tokenMint: mintA, wrappedSolAmountIn: tokenMaxA },
|
||||
{ tokenMint: mintB, wrappedSolAmountIn: tokenMaxB },
|
||||
],
|
||||
() => ctx.fetcher.getAccountRentExempt(),
|
||||
wallet.publicKey,
|
||||
undefined,
|
||||
ctx.accountResolverOpts.allowPDAOwnerAddress,
|
||||
ctx.accountResolverOpts.createWrappedSolAccountMethod,
|
||||
);
|
||||
const { address: tokenOwnerAccountA, ...tokenOwnerAccountAIx } = ataA;
|
||||
const { address: tokenOwnerAccountB, ...tokenOwnerAccountBIx } = ataB;
|
||||
|
||||
txBuilder.addInstruction(tokenOwnerAccountAIx);
|
||||
txBuilder.addInstruction(tokenOwnerAccountBIx);
|
||||
|
||||
const tickArrayLowerStartIndex = TickUtil.getStartTickIndex(
|
||||
tickLowerInitializableIndex,
|
||||
tickSpacing,
|
||||
);
|
||||
const tickArrayUpperStartIndex = TickUtil.getStartTickIndex(
|
||||
tickUpperInitializableIndex,
|
||||
tickSpacing,
|
||||
);
|
||||
const tickArrayLowerPda = PDAUtil.getTickArray(
|
||||
ctx.program.programId,
|
||||
whirlpoolPda.publicKey,
|
||||
tickArrayLowerStartIndex,
|
||||
);
|
||||
const tickArrayUpperPda = PDAUtil.getTickArray(
|
||||
ctx.program.programId,
|
||||
whirlpoolPda.publicKey,
|
||||
tickArrayUpperStartIndex,
|
||||
);
|
||||
if (tickArrayUpperStartIndex !== tickArrayLowerStartIndex) {
|
||||
if (isCorrectMintOrder) {
|
||||
txBuilder.addInstruction(
|
||||
initTickArrayIx(ctx.program, {
|
||||
startTick: tickArrayUpperStartIndex,
|
||||
tickArrayPda: tickArrayUpperPda,
|
||||
whirlpool: whirlpoolPda.publicKey,
|
||||
funder: wallet.publicKey,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
txBuilder.addInstruction(
|
||||
initTickArrayIx(ctx.program, {
|
||||
startTick: tickArrayLowerStartIndex,
|
||||
tickArrayPda: tickArrayLowerPda,
|
||||
whirlpool: whirlpoolPda.publicKey,
|
||||
funder: wallet.publicKey,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const baseParamsLiquidity = {
|
||||
liquidityAmount: liquidity,
|
||||
tokenMaxA,
|
||||
tokenMaxB,
|
||||
whirlpool: whirlpoolPda.publicKey,
|
||||
positionAuthority: wallet.publicKey,
|
||||
position: positionPda.publicKey,
|
||||
positionTokenAccount: positionTokenAccountAddress,
|
||||
tokenOwnerAccountA,
|
||||
tokenOwnerAccountB,
|
||||
tokenVaultA: tokenVaultAKeypair.publicKey,
|
||||
tokenVaultB: tokenVaultBKeypair.publicKey,
|
||||
tickArrayLower: tickArrayLowerPda.publicKey,
|
||||
tickArrayUpper: tickArrayUpperPda.publicKey,
|
||||
};
|
||||
|
||||
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(
|
||||
tokenExtensionCtx,
|
||||
)
|
||||
? increaseLiquidityIx(ctx.program, baseParamsLiquidity)
|
||||
: increaseLiquidityV2Ix(ctx.program, {
|
||||
...baseParamsLiquidity,
|
||||
tokenMintA: mintA,
|
||||
tokenMintB: mintB,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
});
|
||||
txBuilder.addInstruction(liquidityIx);
|
||||
|
||||
const txId = await txBuilder.buildAndExecute();
|
||||
|
||||
return txId;
|
||||
}
|
||||
@@ -15,3 +15,5 @@ export * from "./get_token_data";
|
||||
export * from "./stake_with_jup";
|
||||
export * from "./fetch_price";
|
||||
export * from "./send_compressed_airdrop";
|
||||
|
||||
export * from "./create_orca_single_sided_whirlpool";
|
||||
Reference in New Issue
Block a user