mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-06-05 15:11:44 +00:00
Add compatibility with devnet. Update pool creation to support SOL wrapping. Update send tx function.
This commit is contained in:
@@ -751,15 +751,15 @@ export class SolanaCompressedAirdropTool extends Tool {
|
|||||||
|
|
||||||
export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
|
export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
|
||||||
name = "create_orca_single_sided_whirlpool";
|
name = "create_orca_single_sided_whirlpool";
|
||||||
description = `Create a single-sided Whirlpool with liquidity.
|
description = `Create a single-sided liquidity pools with liquidity on Orca.
|
||||||
|
|
||||||
Inputs (input is a JSON string):
|
Inputs (input is a JSON string):
|
||||||
- depositTokenAmount: number, eg: 1000000000 (required, in units of deposit token including decimals)
|
- depositTokenAmount: number, in units of deposit token including decimals, eg: 1000000000 (required)
|
||||||
- depositTokenMint: string, eg: "DepositTokenMintAddress" (required, mint address of deposit token)
|
- depositTokenMint: string, mint address of deposit token, eg: "DepositTokenMintAddress" (required)
|
||||||
- otherTokenMint: string, eg: "OtherTokenMintAddress" (required, mint address of other token)
|
- otherTokenMint: string, mint address of other token, eg: "OtherTokenMintAddress" (required)
|
||||||
- initialPrice: number, eg: 0.001 (required, initial price of deposit token in terms of other token)
|
- initialPrice: number, initial price of deposit token in terms of other token, eg: 0.001, (required)
|
||||||
- maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added)
|
- maxPrice: number, maximum price at which liquidity is added, eg: 5.0 (required)
|
||||||
- feeTier: number, eg: 0.30 (required, fee tier for the pool)`;
|
- feeTier: number, fee tier for the pool in %. Possible values on mainnet are: 0.01, 0.02, 0.04, 0.05, 0.16, 0.30, 0.65, 1.0, 2.0 (required)`;
|
||||||
|
|
||||||
constructor(private solanaKit: SolanaAgentKit) {
|
constructor(private solanaKit: SolanaAgentKit) {
|
||||||
super();
|
super();
|
||||||
@@ -803,7 +803,6 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class SolanaRaydiumCreateAmmV4 extends Tool {
|
export class SolanaRaydiumCreateAmmV4 extends Tool {
|
||||||
name = "raydium_create_ammV4";
|
name = "raydium_create_ammV4";
|
||||||
description = `Raydium's Legacy AMM that requiers an OpenBook marketID
|
description = `Raydium's Legacy AMM that requiers an OpenBook marketID
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
import { Keypair, PublicKey, Transaction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
||||||
import { SolanaAgentKit } from "../agent";
|
import { SolanaAgentKit } from "../agent";
|
||||||
import { BN, Wallet } from "@coral-xyz/anchor";
|
import { BN, Wallet } from "@coral-xyz/anchor";
|
||||||
import { Decimal } from "decimal.js";
|
import { Decimal } from "decimal.js";
|
||||||
import {
|
import {
|
||||||
PDAUtil,
|
PDAUtil,
|
||||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||||
ORCA_WHIRLPOOLS_CONFIG,
|
|
||||||
WhirlpoolContext,
|
WhirlpoolContext,
|
||||||
TickUtil,
|
TickUtil,
|
||||||
PriceMath,
|
PriceMath,
|
||||||
@@ -110,7 +109,7 @@ export const FEE_TIERS = {
|
|||||||
* const otherTokenMint = new PublicKey("OTHER_TOKEN_ADDRESS");
|
* const otherTokenMint = new PublicKey("OTHER_TOKEN_ADDRESS");
|
||||||
* const initialPrice = new Decimal(0.001);
|
* const initialPrice = new Decimal(0.001);
|
||||||
* const maxPrice = new Decimal(5.0);
|
* const maxPrice = new Decimal(5.0);
|
||||||
* const feeTier = 0.30;
|
* const feeTier = 0.02;
|
||||||
*
|
*
|
||||||
* const txId = await createOrcaSingleSidedWhirlpool(
|
* const txId = await createOrcaSingleSidedWhirlpool(
|
||||||
* agent,
|
* agent,
|
||||||
@@ -133,6 +132,14 @@ export async function createOrcaSingleSidedWhirlpool(
|
|||||||
maxPrice: Decimal,
|
maxPrice: Decimal,
|
||||||
feeTier: keyof typeof FEE_TIERS,
|
feeTier: keyof typeof FEE_TIERS,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
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 wallet = new Wallet(agent.wallet);
|
||||||
const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID);
|
const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID);
|
||||||
const fetcher = ctx.fetcher;
|
const fetcher = ctx.fetcher;
|
||||||
@@ -163,7 +170,7 @@ export async function createOrcaSingleSidedWhirlpool(
|
|||||||
};
|
};
|
||||||
const feeTierKey = PDAUtil.getFeeTier(
|
const feeTierKey = PDAUtil.getFeeTier(
|
||||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||||
ORCA_WHIRLPOOLS_CONFIG,
|
whirlpoolsConfigAddress,
|
||||||
tickSpacing,
|
tickSpacing,
|
||||||
).publicKey;
|
).publicKey;
|
||||||
const initSqrtPrice = PriceMath.tickIndexToSqrtPriceX64(initialTick);
|
const initSqrtPrice = PriceMath.tickIndexToSqrtPriceX64(initialTick);
|
||||||
@@ -171,24 +178,24 @@ export async function createOrcaSingleSidedWhirlpool(
|
|||||||
const tokenVaultBKeypair = Keypair.generate();
|
const tokenVaultBKeypair = Keypair.generate();
|
||||||
const whirlpoolPda = PDAUtil.getWhirlpool(
|
const whirlpoolPda = PDAUtil.getWhirlpool(
|
||||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||||
ORCA_WHIRLPOOLS_CONFIG,
|
whirlpoolsConfigAddress,
|
||||||
mintA,
|
mintA,
|
||||||
mintB,
|
mintB,
|
||||||
FEE_TIERS[feeTier],
|
FEE_TIERS[feeTier],
|
||||||
);
|
);
|
||||||
const tokenBadgeA = PDAUtil.getTokenBadge(
|
const tokenBadgeA = PDAUtil.getTokenBadge(
|
||||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||||
ORCA_WHIRLPOOLS_CONFIG,
|
whirlpoolsConfigAddress,
|
||||||
mintA,
|
mintA,
|
||||||
).publicKey;
|
).publicKey;
|
||||||
const tokenBadgeB = PDAUtil.getTokenBadge(
|
const tokenBadgeB = PDAUtil.getTokenBadge(
|
||||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||||
ORCA_WHIRLPOOLS_CONFIG,
|
whirlpoolsConfigAddress,
|
||||||
mintB,
|
mintB,
|
||||||
).publicKey;
|
).publicKey;
|
||||||
const baseParamsPool = {
|
const baseParamsPool = {
|
||||||
initSqrtPrice,
|
initSqrtPrice,
|
||||||
whirlpoolsConfig: ORCA_WHIRLPOOLS_CONFIG,
|
whirlpoolsConfig: whirlpoolsConfigAddress,
|
||||||
whirlpoolPda,
|
whirlpoolPda,
|
||||||
tokenMintA: mintA,
|
tokenMintA: mintA,
|
||||||
tokenMintB: mintB,
|
tokenMintB: mintB,
|
||||||
@@ -301,7 +308,7 @@ export async function createOrcaSingleSidedWhirlpool(
|
|||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
undefined,
|
undefined,
|
||||||
ctx.accountResolverOpts.allowPDAOwnerAddress,
|
ctx.accountResolverOpts.allowPDAOwnerAddress,
|
||||||
ctx.accountResolverOpts.createWrappedSolAccountMethod,
|
"ata",
|
||||||
);
|
);
|
||||||
const { address: tokenOwnerAccountA, ...tokenOwnerAccountAIx } = ataA;
|
const { address: tokenOwnerAccountA, ...tokenOwnerAccountAIx } = ataA;
|
||||||
const { address: tokenOwnerAccountB, ...tokenOwnerAccountBIx } = ataB;
|
const { address: tokenOwnerAccountB, ...tokenOwnerAccountBIx } = ataB;
|
||||||
@@ -378,22 +385,18 @@ export async function createOrcaSingleSidedWhirlpool(
|
|||||||
});
|
});
|
||||||
txBuilder.addInstruction(liquidityIx);
|
txBuilder.addInstruction(liquidityIx);
|
||||||
|
|
||||||
const txPayload = await txBuilder.build({
|
const txPayload = await txBuilder.build();
|
||||||
maxSupportedTransactionVersion: "legacy"
|
const instructions = TransactionMessage.decompile(
|
||||||
});
|
(txPayload.transaction as VersionedTransaction).message).instructions
|
||||||
|
|
||||||
if (txPayload.transaction instanceof Transaction) {
|
try {
|
||||||
try {
|
const txId = await sendTx(
|
||||||
const txId = await sendTx(
|
agent,
|
||||||
agent,
|
instructions,
|
||||||
txPayload.transaction,
|
[positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair],
|
||||||
[positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair],
|
);
|
||||||
);
|
return txId;
|
||||||
return txId;
|
} catch (error) {
|
||||||
} catch (error) {
|
throw new Error(`Failed to create pool: ${JSON.stringify(error)}`);
|
||||||
throw new Error(`Failed to create pool: ${JSON.stringify(error)}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Failed to create pool: Transaction not created');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,60 @@
|
|||||||
import { SolanaAgentKit } from "../agent";
|
import { SolanaAgentKit } from "../agent";
|
||||||
import { Transaction, Keypair, TransactionInstruction } from "@solana/web3.js";
|
import { Keypair, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
||||||
import { Connection, ComputeBudgetProgram } from "@solana/web3.js";
|
import { Connection, ComputeBudgetProgram, } from "@solana/web3.js";
|
||||||
|
|
||||||
|
|
||||||
|
const feeTiers = {
|
||||||
|
min: 1,
|
||||||
|
mid: 0.5,
|
||||||
|
max: 0.95
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get priority fees for the current block
|
* Get priority fees for the current block
|
||||||
* @param connection - Solana RPC connection
|
* @param connection - Solana RPC connection
|
||||||
* @returns Priority fees statistics and instructions for different fee levels
|
* @returns Priority fees statistics and instructions for different fee levels
|
||||||
*/
|
*/
|
||||||
export async function getPriorityFees(connection: Connection): Promise<{
|
export async function getComputeBudgetInstructions(agent: SolanaAgentKit, instructions: TransactionInstruction[], feeTier: keyof typeof feeTiers): Promise<{
|
||||||
min: number;
|
blockhash: string;
|
||||||
median: number;
|
computeBudgetLimitInstruction: TransactionInstruction;
|
||||||
max: number;
|
computeBudgetPriorityFeeInstructions: TransactionInstruction;
|
||||||
instructions?: {
|
} > {
|
||||||
low: TransactionInstruction;
|
|
||||||
medium: TransactionInstruction;
|
|
||||||
high: TransactionInstruction;
|
|
||||||
};
|
|
||||||
}> {
|
|
||||||
try {
|
try {
|
||||||
// Get recent prioritization fees
|
const blockhash = (await agent.connection.getLatestBlockhash()).blockhash;
|
||||||
const priorityFees = await connection.getRecentPrioritizationFees();
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
if (!priorityFees.length) {
|
const priorityFee = await agent.connection.getRecentPrioritizationFees()
|
||||||
return {
|
.then(fees =>
|
||||||
min: 0,
|
fees.sort((a, b) => a.prioritizationFee - b.prioritizationFee)
|
||||||
median: 0,
|
[Math.floor(fees.length * feeTiers[feeTier])].prioritizationFee
|
||||||
max: 0,
|
);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort fees by value
|
const computeBudgetPriorityFeeInstructions = ComputeBudgetProgram.setComputeUnitPrice({
|
||||||
const sortedFees = priorityFees
|
microLamports: priorityFee,
|
||||||
.map((x) => x.prioritizationFee)
|
});
|
||||||
.sort((a, b) => a - b);
|
|
||||||
|
|
||||||
// Calculate statistics
|
|
||||||
const min = sortedFees[0] ?? 0;
|
|
||||||
const max = sortedFees[sortedFees.length - 1] ?? 0;
|
|
||||||
const mid = Math.floor(sortedFees.length / 2);
|
|
||||||
const median =
|
|
||||||
sortedFees.length % 2 === 0
|
|
||||||
? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2
|
|
||||||
: sortedFees[mid] ?? 0;
|
|
||||||
|
|
||||||
// Helper to create priority fee IX based on chosen strategy
|
|
||||||
const createPriorityFeeIx = (fee: number) => {
|
|
||||||
return ComputeBudgetProgram.setComputeUnitPrice({
|
|
||||||
microLamports: fee,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
min,
|
blockhash,
|
||||||
median,
|
computeBudgetLimitInstruction,
|
||||||
max,
|
computeBudgetPriorityFeeInstructions
|
||||||
// Return instructions for different fee levels
|
|
||||||
instructions: {
|
|
||||||
low: createPriorityFeeIx(min),
|
|
||||||
medium: createPriorityFeeIx(median),
|
|
||||||
high: createPriorityFeeIx(max),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting priority fees:", error);
|
console.error("Error getting compute budget instructions fees:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,24 +67,50 @@ export async function getPriorityFees(connection: Connection): Promise<{
|
|||||||
*/
|
*/
|
||||||
export async function sendTx(
|
export async function sendTx(
|
||||||
agent: SolanaAgentKit,
|
agent: SolanaAgentKit,
|
||||||
tx: Transaction,
|
instructions: TransactionInstruction[],
|
||||||
otherKeypairs?: Keypair[]
|
otherKeypairs?: Keypair[]
|
||||||
) {
|
) {
|
||||||
tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash;
|
console.log(instructions)
|
||||||
tx.feePayer = agent.wallet_address;
|
|
||||||
const fees = await getPriorityFees(agent.connection);
|
|
||||||
if (fees.instructions) {
|
|
||||||
tx.add(fees.instructions.medium!);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.sign(agent.wallet, ...(otherKeypairs ?? []));
|
const ixComputeBudget = await getComputeBudgetInstructions(agent, instructions, "mid");
|
||||||
let txid = await agent.connection.sendRawTransaction(tx.serialize());
|
const allInstructions = [
|
||||||
await agent.connection.confirmTransaction({
|
ixComputeBudget.computeBudgetLimitInstruction,
|
||||||
signature: txid,
|
ixComputeBudget.computeBudgetPriorityFeeInstructions,
|
||||||
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
|
...instructions];
|
||||||
lastValidBlockHeight: (
|
const messageV0 = new TransactionMessage({
|
||||||
await agent.connection.getLatestBlockhash()
|
payerKey: agent.wallet_address,
|
||||||
).lastValidBlockHeight,
|
recentBlockhash: ixComputeBudget.blockhash,
|
||||||
});
|
instructions: allInstructions,
|
||||||
return txid;
|
}).compileToV0Message();
|
||||||
|
const transaction = new VersionedTransaction(messageV0);
|
||||||
|
transaction.sign([agent.wallet, ...(otherKeypairs ?? [])]);
|
||||||
|
|
||||||
|
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: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Transaction timeout");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const WALLET_DATA_FILE = "wallet_data.txt";
|
|||||||
async function initializeAgent() {
|
async function initializeAgent() {
|
||||||
try {
|
try {
|
||||||
const llm = new ChatOpenAI({
|
const llm = new ChatOpenAI({
|
||||||
modelName: "gpt-4o-mini",
|
modelName: "gpt-4o",
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user