mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-18 15:10:33 +00:00
chore: lint
This commit is contained in:
@@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 Orca’s 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user