chore: lint

This commit is contained in:
aryan
2024-12-30 14:37:31 +05:30
parent 43953f262f
commit 4c0124da97
11 changed files with 345 additions and 262 deletions

View File

@@ -1,8 +1,8 @@
export const dynamic = 'force-dynamic';
export const fetchCache = 'force-no-store';
export const dynamic = "force-dynamic";
export const fetchCache = "force-no-store";
export const maxDuration = 300;
import { Bot, webhookCallback } from 'grammy';
import { Bot, webhookCallback } from "grammy";
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
import { ChatOpenAI } from "@langchain/openai";
import { MemorySaver } from "@langchain/langgraph";
@@ -10,10 +10,11 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { HumanMessage } from "@langchain/core/messages";
const token = process.env.TELEGRAM_BOT_TOKEN;
if (!token) throw new Error('TELEGRAM_BOT_TOKEN environment variable not found.');
if (!token) {
throw new Error("TELEGRAM_BOT_TOKEN environment variable not found.");
}
const bot = new Bot(token);
async function initializeAgent(userId: string) {
try {
const llm = new ChatOpenAI({
@@ -24,7 +25,7 @@ async function initializeAgent(userId: string) {
const solanaKit = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL,
process.env.OPENAI_API_KEY!
process.env.OPENAI_API_KEY!,
);
const tools = createSolanaTools(solanaKit);
@@ -51,24 +52,40 @@ async function initializeAgent(userId: string) {
}
}
// Telegram bot handler
bot.on('message:text', async (ctx:any) => {
bot.on("message:text", async (ctx: any) => {
const userId = ctx.from?.id.toString();
if (!userId) return;
const {agent, config} = await initializeAgent(userId);
const stream = await agent.stream({ messages: [new HumanMessage(ctx.message.text)] }, config);
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 20000));
if (!userId) {
return;
}
const { agent, config } = await initializeAgent(userId);
const stream = await agent.stream(
{ messages: [new HumanMessage(ctx.message.text)] },
config,
);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), 20000),
);
try {
for await (const chunk of await Promise.race([stream, timeoutPromise]) as AsyncIterable<{ agent?: any; tools?: any }>) {
for await (const chunk of (await Promise.race([
stream,
timeoutPromise,
])) as AsyncIterable<{ agent?: any; tools?: any }>) {
if ("agent" in chunk) {
if (chunk.agent.messages[0].content) await ctx.reply(String(chunk.agent.messages[0].content));
}
if (chunk.agent.messages[0].content) {
await ctx.reply(String(chunk.agent.messages[0].content));
}
}
}
} catch (error: any) {
if (error.message === 'Timeout') {
await ctx.reply("I'm sorry, the operation took too long and timed out. Please try again.");
if (error.message === "Timeout") {
await ctx.reply(
"I'm sorry, the operation took too long and timed out. Please try again.",
);
} else {
console.error("Error processing stream:", error);
await ctx.reply("I'm sorry, an error occurred while processing your request.");
await ctx.reply(
"I'm sorry, an error occurred while processing your request.",
);
}
}
});
@@ -77,10 +94,10 @@ bot.on('message:text', async (ctx:any) => {
export const POST = async (req: Request) => {
// Mark the function as a background function for Vercel
const headers = new Headers();
headers.set('x-vercel-background', 'true');
headers.set("x-vercel-background", "true");
const handler = webhookCallback(bot, 'std/http'); // Use the correct callback
const handler = webhookCallback(bot, "std/http"); // Use the correct callback
// Handle the incoming webhook request
return handler(req);
};
};

View File

@@ -215,13 +215,8 @@ export class SolanaAgentKit {
);
}
async orcaClosePosition(
positionMintAddress: PublicKey,
) {
return orcaClosePosition(
this,
positionMintAddress,
);
async orcaClosePosition(positionMintAddress: PublicKey) {
return orcaClosePosition(this, positionMintAddress);
}
async orcaCreateCLMM(
@@ -230,13 +225,7 @@ export class SolanaAgentKit {
initialPrice: Decimal,
feeTier: keyof typeof FEE_TIERS,
) {
return orcaCreateCLMM(
this,
mintDeploy,
mintPair,
initialPrice,
feeTier,
);
return orcaCreateCLMM(this, mintDeploy, mintPair, initialPrice, feeTier);
}
async orcaCreateSingleSidedLiquidityPool(
@@ -258,11 +247,8 @@ export class SolanaAgentKit {
);
}
async orcaFetchPositions(
) {
return orcaFetchPositions(
this,
);
async orcaFetchPositions() {
return orcaFetchPositions(this);
}
async orcaOpenCenteredPositionWithLiquidity(

View File

@@ -798,11 +798,11 @@ export class SolanaCompressedAirdropTool extends Tool {
export class SolanaClosePostition extends Tool {
name = "orca_close_position";
description = `Closes an existing liquidity position in an Orca Whirlpool. This function fetches the position
description = `Closes an existing liquidity position in an Orca Whirlpool. This function fetches the position
details using the provided mint address and closes the position with a 1% slippage.
Inputs (JSON string):
- positionMintAddress: string, the address of the position mint that represents the liquidity position.`
- positionMintAddress: string, the address of the position mint that represents the liquidity position.`;
constructor(private solanaKit: SolanaAgentKit) {
super();
@@ -811,12 +811,12 @@ export class SolanaClosePostition extends Tool {
async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const positionMintAddress = new PublicKey(inputFormat.positionMintAddress);
const txId = await this.solanaKit.orcaClosePosition(
positionMintAddress,
const positionMintAddress = new PublicKey(
inputFormat.positionMintAddress,
);
const txId = await this.solanaKit.orcaClosePosition(positionMintAddress);
return JSON.stringify({
status: "success",
message: "Liquidity position closed successfully.",
@@ -871,7 +871,8 @@ export class SolanaOrcaCreateCLMM extends Tool {
return JSON.stringify({
status: "success",
message: "CLMM pool created successfully. Note: No liquidity was added.",
message:
"CLMM pool created successfully. Note: No liquidity was added.",
transaction: txId,
});
} catch (error: any) {
@@ -884,7 +885,6 @@ export class SolanaOrcaCreateCLMM extends Tool {
}
}
export class SolanaOrcaCreateSingleSideLiquidityPool extends Tool {
name = "orca_create_single_sided_liquidity_pool";
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
@@ -955,8 +955,7 @@ export class SolanaOrcaFetchPositions extends Tool {
async _call(): Promise<string> {
try {
const txId = await this.solanaKit.orcaFetchPositions()
const txId = await this.solanaKit.orcaFetchPositions();
return JSON.stringify({
status: "success",
@@ -995,8 +994,10 @@ export class SolanaOrcaOpenCenteredPosition extends Tool {
const inputTokenMint = new PublicKey(inputFormat.inputTokenMint);
const inputAmount = new Decimal(inputFormat.inputAmount);
if (priceOffsetBps < 0 ) {
throw new Error("Invalid distanceFromCurrentPriceBps. It must be equal or greater than 0.");
if (priceOffsetBps < 0) {
throw new Error(
"Invalid distanceFromCurrentPriceBps. It must be equal or greater than 0.",
);
}
const txId = await this.solanaKit.orcaOpenCenteredPositionWithLiquidity(
@@ -1040,13 +1041,16 @@ export class SolanaOrcaOpenSingleSidedPosition extends Tool {
try {
const inputFormat = JSON.parse(input);
const whirlpoolAddress = new PublicKey(inputFormat.whirlpoolAddress);
const distanceFromCurrentPriceBps = inputFormat.distanceFromCurrentPriceBps;
const distanceFromCurrentPriceBps =
inputFormat.distanceFromCurrentPriceBps;
const widthBps = inputFormat.widthBps;
const inputTokenMint = new PublicKey(inputFormat.inputTokenMint);
const inputAmount = new Decimal(inputFormat.inputAmount);
if (distanceFromCurrentPriceBps < 0 || widthBps < 0) {
throw new Error("Invalid distanceFromCurrentPriceBps or width. It must be equal or greater than 0.");
throw new Error(
"Invalid distanceFromCurrentPriceBps or width. It must be equal or greater than 0.",
);
}
const txId = await this.solanaKit.orcaOpenSingleSidedPosition(
@@ -1520,9 +1524,9 @@ export class SolanaRockPaperScissorsTool extends Tool {
const result = await this.solanaKit.rockPaperScissors(
Number(parsedInput['"amount"']),
parsedInput['"choice"'].replace(/^"|"$/g, "") as
| "rock"
| "paper"
| "scissors",
| "rock"
| "paper"
| "scissors",
);
return JSON.stringify({

View File

@@ -16,7 +16,7 @@ export * from "./get_token_data";
export * from "./stake_with_jup";
export * from "./fetch_price";
export * from "./send_compressed_airdrop";
export * from "./orca_close_position"
export * from "./orca_close_position";
export * from "./orca_create_clmm";
export * from "./orca_create_single_sided_liquidity_pool";
export * from "./orca_fetch_positions";

View File

@@ -2,7 +2,7 @@ import {
Keypair,
PublicKey,
TransactionMessage,
VersionedTransaction
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
@@ -32,7 +32,7 @@ import { Percentage } from "@orca-so/common-sdk";
* - The function uses Orcas SDK to interact with the specified Whirlpool and close the liquidity position.
* - A maximum slippage of 1% is assumed for liquidity provision during the position closing.
* - The function automatically fetches the associated Whirlpool address and position details using the provided mint address.
*
*
* ## Throws
* An error will be thrown if:
* - The specified position mint address is invalid or inaccessible.
@@ -54,25 +54,29 @@ export async function orcaClosePosition(
wallet,
ORCA_WHIRLPOOL_PROGRAM_ID,
);
const client = buildWhirlpoolClient(ctx)
const client = buildWhirlpoolClient(ctx);
const positionAddress = PDAUtil.getPosition(ORCA_WHIRLPOOL_PROGRAM_ID, positionMintAddress);
const positionAddress = PDAUtil.getPosition(
ORCA_WHIRLPOOL_PROGRAM_ID,
positionMintAddress,
);
const position = await client.getPosition(positionAddress.publicKey);
const whirlpoolAddress = position.getData().whirlpool;
const whirlpool = await client.getPool(whirlpoolAddress);
const txBuilder = await whirlpool.closePosition(positionAddress.publicKey, Percentage.fromFraction(1, 100));
const txBuilder = await whirlpool.closePosition(
positionAddress.publicKey,
Percentage.fromFraction(1, 100),
);
const txPayload = await txBuilder[0].build();
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
const txPayloadDecompiled = TransactionMessage.decompile(
(txPayload.transaction as VersionedTransaction).message,
);
const instructions = txPayloadDecompiled.instructions;
const signers = txPayload.signers as Keypair[];
const txId = await sendTx(
agent,
instructions,
signers
);
return txId
const txId = await sendTx(agent, instructions, signers);
return txId;
} catch (error) {
throw new Error(`${error}`);
}
}
}

View File

@@ -1,8 +1,8 @@
import {
import {
Keypair,
PublicKey,
TransactionMessage,
VersionedTransaction
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
@@ -45,7 +45,7 @@ import { FEE_TIERS } from "./orca_create_single_sided_liquidity_pool";
*
* @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.
* a separate function after the pool is successfully created.
* ```
*/
export async function orcaCreateCLMM(
@@ -57,12 +57,16 @@ export async function orcaCreateCLMM(
): 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');
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');
throw new Error("Unsupported network");
}
const wallet = new Wallet(agent.wallet);
const ctx = WhirlpoolContext.from(
@@ -71,14 +75,12 @@ export async function orcaCreateCLMM(
ORCA_WHIRLPOOL_PROGRAM_ID,
);
const fetcher = ctx.fetcher;
const client = buildWhirlpoolClient(ctx)
const client = buildWhirlpoolClient(ctx);
const correctTokenOrder = PoolUtil.orderMints(
mintDeploy,
mintPair,
).map((addr) => addr.toString());
const isCorrectMintOrder =
correctTokenOrder[0] === mintDeploy.toString();
const correctTokenOrder = PoolUtil.orderMints(mintDeploy, mintPair).map(
(addr) => addr.toString(),
);
const isCorrectMintOrder = correctTokenOrder[0] === mintDeploy.toString();
let mintA;
let mintB;
if (!isCorrectMintOrder) {
@@ -94,7 +96,12 @@ export async function orcaCreateCLMM(
}
const tickSpacing = FEE_TIERS[feeTier];
const initialTick = PriceMath.priceToInitializableTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals, tickSpacing)
const initialTick = PriceMath.priceToInitializableTickIndex(
initialPrice,
mintAAccount.decimals,
mintBAccount.decimals,
tickSpacing,
);
const { poolKey, tx: txBuilder } = await client.createPool(
whirlpoolsConfigAddress,
mintA,
@@ -102,16 +109,18 @@ export async function orcaCreateCLMM(
tickSpacing,
initialTick,
wallet.publicKey,
)
);
const txPayload = await txBuilder.build();
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
const txPayloadDecompiled = TransactionMessage.decompile(
(txPayload.transaction as VersionedTransaction).message,
);
const instructions = txPayloadDecompiled.instructions;
const txId = await sendTx(
agent,
instructions,
txPayload.signers as Keypair[]
txPayload.signers as Keypair[],
);
return JSON.stringify({
transactionId: txId,
@@ -120,4 +129,4 @@ export async function orcaCreateCLMM(
} catch (error) {
throw new Error(`${error}`);
}
}
}

View File

@@ -1,8 +1,8 @@
import {
Keypair,
PublicKey,
TransactionMessage,
VersionedTransaction
import {
Keypair,
PublicKey,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { BN, Wallet } from "@coral-xyz/anchor";
@@ -112,12 +112,16 @@ export async function orcaCreateSingleSidedLiquidityPool(
): 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');
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');
throw new Error("Unsupported network");
}
const wallet = new Wallet(agent.wallet);
const ctx = WhirlpoolContext.from(
@@ -263,7 +267,7 @@ export async function orcaCreateSingleSidedLiquidityPool(
) {
throw Error("Prices out of bounds");
}
depositTokenAmount = isCorrectMintOrder
depositTokenAmount = isCorrectMintOrder
? depositTokenAmount * Math.pow(10, mintAAccount.decimals)
: depositTokenAmount * Math.pow(10, mintBAccount.decimals);
const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = {
@@ -388,7 +392,9 @@ export async function orcaCreateSingleSidedLiquidityPool(
tickArrayUpper: tickArrayUpperPda.publicKey,
};
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(
tokenExtensionCtx,
)
? increaseLiquidityIx(ctx.program, baseParamsLiquidity)
: increaseLiquidityV2Ix(ctx.program, {
...baseParamsLiquidity,
@@ -404,14 +410,13 @@ export async function orcaCreateSingleSidedLiquidityPool(
(txPayload.transaction as VersionedTransaction).message,
).instructions;
const txId = await sendTx(
agent,
instructions,
[positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair],
);
const txId = await sendTx(agent, instructions, [
positionMintKeypair,
tokenVaultAKeypair,
tokenVaultBKeypair,
]);
return txId;
} catch (error) {
throw new Error(`Failed to send transaction: ${JSON.stringify(error)}`);
}
}
}

View File

@@ -58,18 +58,18 @@ export async function orcaFetchPositions(
wallet,
ORCA_WHIRLPOOL_PROGRAM_ID,
);
const client = buildWhirlpoolClient(ctx)
const client = buildWhirlpoolClient(ctx);
const positions = await getAllPositionAccountsByOwner({
ctx,
owner: agent.wallet.publicKey
})
ctx,
owner: agent.wallet.publicKey,
});
const positionDatas = [
...positions.positions.entries(),
...positions.positionsWithTokenExtensions.entries()
...positions.positionsWithTokenExtensions.entries(),
];
const result: PositionDataMap = {};
for (const [_, positionData] of positionDatas) {
for (const [, positionData] of positionDatas) {
const positionMintAddress = positionData.positionMint;
const whirlpoolAddress = positionData.whirlpool;
const whirlpool = await client.getPool(whirlpoolAddress);
@@ -78,16 +78,34 @@ export async function orcaFetchPositions(
const currentTick = whirlpoolData.tickCurrentIndex;
const mintA = whirlpool.getTokenAInfo();
const mintB = whirlpool.getTokenBInfo();
const currentPrice = PriceMath.sqrtPriceX64ToPrice(sqrtPrice, mintA.decimals, mintB.decimals);
const lowerTick = positionData.tickLowerIndex
const upperTick = positionData.tickUpperIndex
const lowerPrice = PriceMath.tickIndexToPrice(lowerTick, mintA.decimals, mintB.decimals);
const upperPrice = PriceMath.tickIndexToPrice(upperTick, mintA.decimals, mintB.decimals);
const centerPosition = (lowerPrice.add(upperPrice)).div(2);
const currentPrice = PriceMath.sqrtPriceX64ToPrice(
sqrtPrice,
mintA.decimals,
mintB.decimals,
);
const lowerTick = positionData.tickLowerIndex;
const upperTick = positionData.tickUpperIndex;
const lowerPrice = PriceMath.tickIndexToPrice(
lowerTick,
mintA.decimals,
mintB.decimals,
);
const upperPrice = PriceMath.tickIndexToPrice(
upperTick,
mintA.decimals,
mintB.decimals,
);
const centerPosition = lowerPrice.add(upperPrice).div(2);
const positionInRange = (currentTick > lowerTick && currentTick < upperTick) ? true : false;
const positionInRange =
currentTick > lowerTick && currentTick < upperTick ? true : false;
const distanceFromCenterBps = Math.ceil(
currentPrice.sub(centerPosition).abs().div(centerPosition).mul(10000).toNumber()
currentPrice
.sub(centerPosition)
.abs()
.div(centerPosition)
.mul(10000)
.toNumber(),
);
result[positionMintAddress.toString()] = {
@@ -100,4 +118,4 @@ export async function orcaFetchPositions(
} catch (error) {
throw new Error(`${error}`);
}
}
}

View File

@@ -1,4 +1,10 @@
import { Keypair, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import {
Keypair,
PublicKey,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js";
@@ -20,7 +26,7 @@ import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
* # Opens a Centered Liquidity Position in an Orca Whirlpool
*
* This function opens a centered liquidity position in a specified Orca Whirlpool. The user defines
* a basis point (bps) offset from the cuurent price of the pool to set the lower and upper bounds of the position.
* a basis point (bps) offset from the cuurent price of the pool to set the lower and upper bounds of the position.
* The user also specifies the token mint and the amount to deposit. The required amount of the other token
* is calculated automatically.
*
@@ -69,29 +75,44 @@ export async function orcaOpenCenteredPositionWithLiquidity(
wallet,
ORCA_WHIRLPOOL_PROGRAM_ID,
);
const client = buildWhirlpoolClient(ctx)
const client = buildWhirlpoolClient(ctx);
const whirlpool = await client.getPool(whirlpoolAddress);
const whirlpoolData = whirlpool.getData();
const mintInfoA = whirlpool.getTokenAInfo()
const mintInfoB = whirlpool.getTokenBInfo()
const mintInfoA = whirlpool.getTokenAInfo();
const mintInfoB = whirlpool.getTokenBInfo();
const price = PriceMath.sqrtPriceX64ToPrice(
whirlpoolData.sqrtPrice,
mintInfoA.decimals,
mintInfoB.decimals
)
mintInfoB.decimals,
);
const lowerPrice = price.mul(1 - priceOffsetBps / 10000)
const upperPrice = price.mul(1 + priceOffsetBps / 10000)
const lowerTick = PriceMath.priceToInitializableTickIndex(lowerPrice, mintInfoA.decimals, mintInfoB.decimals, whirlpoolData.tickSpacing)
const upperTick = PriceMath.priceToInitializableTickIndex(upperPrice, mintInfoA.decimals, mintInfoB.decimals, whirlpoolData.tickSpacing)
const lowerPrice = price.mul(1 - priceOffsetBps / 10000);
const upperPrice = price.mul(1 + priceOffsetBps / 10000);
const lowerTick = PriceMath.priceToInitializableTickIndex(
lowerPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing,
);
const upperTick = PriceMath.priceToInitializableTickIndex(
upperPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing,
);
const txBuilderTickArrays = await whirlpool.initTickArrayForTicks([lowerTick, upperTick])
let instructions: TransactionInstruction[] = []
let signers: Keypair[] = []
const txBuilderTickArrays = await whirlpool.initTickArrayForTicks([
lowerTick,
upperTick,
]);
let instructions: TransactionInstruction[] = [];
let signers: Keypair[] = [];
if (txBuilderTickArrays !== null) {
const txPayloadTickArrays = await txBuilderTickArrays.build();
const txPayloadTickArraysDecompiled = TransactionMessage.decompile((txPayloadTickArrays.transaction as VersionedTransaction).message);
const txPayloadTickArraysDecompiled = TransactionMessage.decompile(
(txPayloadTickArrays.transaction as VersionedTransaction).message,
);
const instructionsTickArrays = txPayloadTickArraysDecompiled.instructions;
instructions = instructions.concat(instructionsTickArrays);
signers = signers.concat(txPayloadTickArrays.signers as Keypair[]);
@@ -109,33 +130,32 @@ export async function orcaOpenCenteredPositionWithLiquidity(
upperTick,
Percentage.fromFraction(1, 100),
whirlpool,
tokenExtensionCtx
)
const { positionMint, tx: txBuilder } = await whirlpool.openPositionWithMetadata(
lowerTick,
upperTick,
increaseLiquiditQuote,
undefined,
undefined,
undefined,
TOKEN_2022_PROGRAM_ID
)
tokenExtensionCtx,
);
const { positionMint, tx: txBuilder } =
await whirlpool.openPositionWithMetadata(
lowerTick,
upperTick,
increaseLiquiditQuote,
undefined,
undefined,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const txPayload = await txBuilder.build();
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
const txPayloadDecompiled = TransactionMessage.decompile(
(txPayload.transaction as VersionedTransaction).message,
);
instructions = instructions.concat(txPayloadDecompiled.instructions);
signers = signers.concat(txPayload.signers as Keypair[]);
const txId = await sendTx(
agent,
instructions,
signers
);
const txId = await sendTx(agent, instructions, signers);
return JSON.stringify({
transactionId: txId,
positionMint: positionMint.toString(),
})
});
} catch (error) {
throw new Error(`${error}`);
}
}
}

View File

@@ -1,4 +1,9 @@
import { Keypair, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import {
Keypair,
PublicKey,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js";
@@ -52,7 +57,7 @@ export async function orcaOpenSingleSidedPosition(
distanceFromCurrentPriceBps: number,
widthBps: number,
inputTokenMint: PublicKey,
inputAmount: Decimal
inputAmount: Decimal,
): Promise<string> {
try {
const wallet = new Wallet(agent.wallet);
@@ -70,7 +75,7 @@ export async function orcaOpenSingleSidedPosition(
const price = PriceMath.sqrtPriceX64ToPrice(
whirlpoolData.sqrtPrice,
mintInfoA.decimals,
mintInfoB.decimals
mintInfoB.decimals,
);
const isTokenA = inputTokenMint.equals(mintInfoA.mint);
@@ -85,13 +90,13 @@ export async function orcaOpenSingleSidedPosition(
upperBoundPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing
whirlpoolData.tickSpacing,
);
lowerTick = PriceMath.priceToInitializableTickIndex(
lowerBoundPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing
whirlpoolData.tickSpacing,
);
} else {
lowerBoundPrice = price.mul(1 - distanceFromCurrentPriceBps / 10000);
@@ -100,26 +105,31 @@ export async function orcaOpenSingleSidedPosition(
upperBoundPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing
whirlpoolData.tickSpacing,
);
upperTick = PriceMath.priceToInitializableTickIndex(
lowerBoundPrice,
mintInfoA.decimals,
mintInfoB.decimals,
whirlpoolData.tickSpacing
whirlpoolData.tickSpacing,
);
}
const txBuilderTickArrays = await whirlpool.initTickArrayForTicks([lowerTick, upperTick]);
let txIds: string = '';
const txBuilderTickArrays = await whirlpool.initTickArrayForTicks([
lowerTick,
upperTick,
]);
let txIds: string = "";
if (txBuilderTickArrays !== null) {
const txPayloadTickArrays = await txBuilderTickArrays.build();
const txPayloadTickArraysDecompiled = TransactionMessage.decompile((txPayloadTickArrays.transaction as VersionedTransaction).message);
const txPayloadTickArraysDecompiled = TransactionMessage.decompile(
(txPayloadTickArrays.transaction as VersionedTransaction).message,
);
const instructions = txPayloadTickArraysDecompiled.instructions;
const signers = txPayloadTickArrays.signers as Keypair[];
const tickArrayTxId = await sendTx(agent, instructions, signers);
txIds += tickArrayTxId + ',';
txIds += tickArrayTxId + ",";
}
const tokenExtensionCtx: TokenExtensionContextForPool = {
@@ -134,25 +144,28 @@ export async function orcaOpenSingleSidedPosition(
upperTick,
Percentage.fromFraction(1, 100),
whirlpool,
tokenExtensionCtx
);
const { positionMint, tx: txBuilder } = await whirlpool.openPositionWithMetadata(
lowerTick,
upperTick,
increaseLiquiditQuote,
undefined,
undefined,
undefined,
TOKEN_2022_PROGRAM_ID
tokenExtensionCtx,
);
const { positionMint, tx: txBuilder } =
await whirlpool.openPositionWithMetadata(
lowerTick,
upperTick,
increaseLiquiditQuote,
undefined,
undefined,
undefined,
TOKEN_2022_PROGRAM_ID,
);
const txPayload = await txBuilder.build();
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
const txPayloadDecompiled = TransactionMessage.decompile(
(txPayload.transaction as VersionedTransaction).message,
);
const instructions = txPayloadDecompiled.instructions;
const signers = txPayload.signers as Keypair[];
const positionTxId = await sendTx(agent, instructions, signers);
txIds += positionTxId;
txIds += positionTxId;
return JSON.stringify({
transactionIds: txIds,

View File

@@ -1,61 +1,71 @@
import { SolanaAgentKit } from "../agent";
import { Keypair, Signer, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import { ComputeBudgetProgram, } from "@solana/web3.js";
import {
Keypair,
Signer,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
const feeTiers = {
min: 0.01,
mid: 0.5,
max: 0.95
}
max: 0.95,
};
/**
* Get priority fees for the current block
* @param connection - Solana RPC connection
* @returns Priority fees statistics and instructions for different fee levels
*/
export async function getComputeBudgetInstructions(agent: SolanaAgentKit, instructions: TransactionInstruction[], feeTier: keyof typeof feeTiers): Promise<{
export async function getComputeBudgetInstructions(
agent: SolanaAgentKit,
instructions: TransactionInstruction[],
feeTier: keyof typeof feeTiers,
): Promise<{
blockhash: string;
computeBudgetLimitInstruction: TransactionInstruction;
computeBudgetPriorityFeeInstructions: TransactionInstruction;
} > {
try {
const blockhash = (await agent.connection.getLatestBlockhash()).blockhash;
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: blockhash,
instructions: instructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
const simulatedTx = agent.connection.simulateTransaction(transaction);
const estimatedComputeUnits = (await simulatedTx).value.unitsConsumed;
const safeComputeUnits = Math.ceil(
estimatedComputeUnits ?
Math.max(estimatedComputeUnits + 100000, estimatedComputeUnits * 1.2)
: 200000
);
const computeBudgetLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
}> {
const blockhash = (await agent.connection.getLatestBlockhash()).blockhash;
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: blockhash,
instructions: instructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
const simulatedTx = agent.connection.simulateTransaction(transaction);
const estimatedComputeUnits = (await simulatedTx).value.unitsConsumed;
const safeComputeUnits = Math.ceil(
estimatedComputeUnits
? Math.max(estimatedComputeUnits + 100000, estimatedComputeUnits * 1.2)
: 200000,
);
const computeBudgetLimitInstruction =
ComputeBudgetProgram.setComputeUnitLimit({
units: safeComputeUnits,
});
const priorityFee = await agent.connection.getRecentPrioritizationFees()
.then(fees =>
fees.sort((a, b) => a.prioritizationFee - b.prioritizationFee)
[Math.floor(fees.length * feeTiers[feeTier])].prioritizationFee
);
const priorityFee = await agent.connection
.getRecentPrioritizationFees()
.then(
(fees) =>
fees.sort((a, b) => a.prioritizationFee - b.prioritizationFee)[
Math.floor(fees.length * feeTiers[feeTier])
].prioritizationFee,
);
const computeBudgetPriorityFeeInstructions = ComputeBudgetProgram.setComputeUnitPrice({
const computeBudgetPriorityFeeInstructions =
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
});
return {
blockhash,
computeBudgetLimitInstruction,
computeBudgetPriorityFeeInstructions
};
} catch (error) {
throw error;
}
return {
blockhash,
computeBudgetLimitInstruction,
computeBudgetPriorityFeeInstructions,
};
}
/**
@@ -67,55 +77,52 @@ export async function getComputeBudgetInstructions(agent: SolanaAgentKit, instru
export async function sendTx(
agent: SolanaAgentKit,
instructions: TransactionInstruction[],
otherKeypairs?: Keypair[]
otherKeypairs?: Keypair[],
) {
try {
const ixComputeBudget = await getComputeBudgetInstructions(agent, instructions, "mid");
const allInstructions = [
ixComputeBudget.computeBudgetLimitInstruction,
ixComputeBudget.computeBudgetPriorityFeeInstructions,
...instructions];
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: ixComputeBudget.blockhash,
instructions: allInstructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([agent.wallet, ...(otherKeypairs ?? [])] as Signer[]);
const ixComputeBudget = await getComputeBudgetInstructions(
agent,
instructions,
"mid",
);
const allInstructions = [
ixComputeBudget.computeBudgetLimitInstruction,
ixComputeBudget.computeBudgetPriorityFeeInstructions,
...instructions,
];
const messageV0 = new TransactionMessage({
payerKey: agent.wallet_address,
recentBlockhash: ixComputeBudget.blockhash,
instructions: allInstructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(messageV0);
transaction.sign([agent.wallet, ...(otherKeypairs ?? [])] as Signer[]);
const timeoutMs = 90000;
const startTime = Date.now();
try {
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
const timeoutMs = 90000;
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const transactionStartTime = Date.now();
const signature = await agent.connection.sendTransaction(
transaction,
{
maxRetries: 0,
skipPreflight: false,
});
const signature = await agent.connection.sendTransaction(transaction, {
maxRetries: 0,
skipPreflight: false,
});
const statuses = await agent.connection.getSignatureStatuses([signature]);
if (statuses.value[0]) {
if (!statuses.value[0].err) {
return signature;
} else {
throw new Error(`Transaction failed: ${statuses.value[0].err.toString()}`);
}
}
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise(resolve => setTimeout(resolve, remainingTime));
}
const statuses = await agent.connection.getSignatureStatuses([signature]);
if (statuses.value[0]) {
if (!statuses.value[0].err) {
return signature;
} else {
throw new Error(
`Transaction failed: ${statuses.value[0].err.toString()}`,
);
}
throw new Error("Transaction timeout");
} catch (error) {
throw error;
}
} catch (error) {
throw error;
const elapsedTime = Date.now() - transactionStartTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
if (remainingTime > 0) {
await new Promise((resolve) => setTimeout(resolve, remainingTime));
}
}
throw new Error("Transaction timeout");
}