mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-06-03 15:11:32 +00:00
Add Orca tools.
This commit is contained in:
117
src/tools/orca_create_clmm.ts
Normal file
117
src/tools/orca_create_clmm.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { Keypair, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { Wallet } from "@coral-xyz/anchor";
|
||||
import { Decimal } from "decimal.js";
|
||||
import {
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
WhirlpoolContext,
|
||||
PriceMath,
|
||||
PoolUtil,
|
||||
buildWhirlpoolClient,
|
||||
} from "@orca-so/whirlpools-sdk";
|
||||
|
||||
import { sendTx } from "../utils/send_tx";
|
||||
import { FEE_TIERS } from "./orca_create_single_sided_liquidity_pool";
|
||||
|
||||
/**
|
||||
* # Creates a CLMM Pool (Concentrated Liquidity Market Maker Pool).
|
||||
*
|
||||
* This function initializes a new Whirlpool (CLMM Pool) on Orca. It only sets up the pool and does not seed it with liquidity.
|
||||
*
|
||||
* ## Example Usage:
|
||||
* Suppose you want to create a CLMM pool with two tokens, SHARK and USDC, and set the initial price of SHARK to 0.001 USDC.
|
||||
* You would call this function with `mintA` as SHARK's mint address and `mintB` as USDC's mint address. The pool is created
|
||||
* with the specified fee tier and tick spacing associated with that fee tier.
|
||||
*
|
||||
* ### Note for Experts:
|
||||
* The Whirlpool program determines the token mint order, which might not match your expectation. This function
|
||||
* adjusts the input order as needed and inverts the initial price accordingly.
|
||||
*
|
||||
* @param agent - The `SolanaAgentKit` instance representing the wallet and connection details.
|
||||
* @param mintA - The mint address of the first token in the pool (e.g., SHARK).
|
||||
* @param mintB - The mint address of the second token in the pool (e.g., USDC).
|
||||
* @param initialPrice - The initial price of `mintA` in terms of `mintB`.
|
||||
* @param feeTier - The fee tier bps 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.
|
||||
* - The network is unsupported.
|
||||
*
|
||||
* @remarks
|
||||
* This function only initializes the CLMM pool and does not add liquidity. For adding liquidity, you can use
|
||||
* a separate function after the pool is successfully created.
|
||||
* ```
|
||||
*/
|
||||
export async function orcaCreateCLMM(
|
||||
agent: SolanaAgentKit,
|
||||
mintA: PublicKey,
|
||||
mintB: PublicKey,
|
||||
initialPrice: Decimal,
|
||||
feeTier: keyof typeof FEE_TIERS,
|
||||
): Promise<string> {
|
||||
try {
|
||||
let whirlpoolsConfigAddress: PublicKey;
|
||||
if (agent.connection.rpcEndpoint.includes('mainnet')) {
|
||||
whirlpoolsConfigAddress = new PublicKey('2LecshUwdy9xi7meFgHtFJQNSKk4KdTrcpvaB56dP2NQ');
|
||||
} else if (agent.connection.rpcEndpoint.includes('devnet')) {
|
||||
whirlpoolsConfigAddress = new PublicKey('FcrweFY1G9HJAHG5inkGB6pKg1HZ6x9UC2WioAfWrGkR');
|
||||
} else {
|
||||
throw new Error('Unsupported network');
|
||||
}
|
||||
const wallet = new Wallet(agent.wallet);
|
||||
const ctx = WhirlpoolContext.from(
|
||||
agent.connection,
|
||||
wallet,
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
);
|
||||
const fetcher = ctx.fetcher;
|
||||
const client = buildWhirlpoolClient(ctx)
|
||||
|
||||
const correctTokenOrder = PoolUtil.orderMints(
|
||||
mintA,
|
||||
mintB,
|
||||
).map((addr) => addr.toString());
|
||||
const isCorrectMintOrder =
|
||||
correctTokenOrder[0] === mintA.toString();
|
||||
if (!isCorrectMintOrder) {
|
||||
[mintA, mintB] = [mintB, mintA];
|
||||
initialPrice = new Decimal(1 / initialPrice.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 initialTick = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals)
|
||||
const tickSpacing = FEE_TIERS[feeTier];
|
||||
|
||||
const { poolKey, tx: txBuilder } = await client.createPool(
|
||||
whirlpoolsConfigAddress,
|
||||
mintA,
|
||||
mintB,
|
||||
tickSpacing,
|
||||
initialTick,
|
||||
wallet.publicKey,
|
||||
)
|
||||
|
||||
const txPayload = await txBuilder.build();
|
||||
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
|
||||
const instructions = txPayloadDecompiled.instructions;
|
||||
|
||||
const txId = await sendTx(
|
||||
agent,
|
||||
instructions,
|
||||
txPayload.signers as Keypair[]
|
||||
);
|
||||
return JSON.stringify({
|
||||
transactionId: txId,
|
||||
whirlpoolAddress: poolKey.toString(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw new Error(`${error}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user