feat: Refactor actions to use tool functions for improved code clarity and maintainability

This commit is contained in:
Fahri Bilici
2024-12-28 16:24:57 +01:00
parent 9326da25b1
commit 378fea201d
30 changed files with 118 additions and 654 deletions

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { get_balance } from "../tools";
const balanceAction: Action = {
name: "solana_balance",
@@ -45,8 +46,7 @@ const balanceAction: Action = {
tokenAddress: z.string().optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const tokenAddress = input.tokenAddress ? new PublicKey(input.tokenAddress) : undefined;
const balance = await agent.getBalance(tokenAddress);
const balance = await get_balance(agent, input.tokenAddress && new PublicKey(input.tokenAddress));
return {
status: "success",

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
import { create_gibwork_task } from "../tools";
const createGibworkTaskAction: Action = {
name: "solana_create_gibwork_task",
@@ -62,65 +63,21 @@ const createGibworkTaskAction: Action = {
const tokenMintAddress = new PublicKey(input.tokenMintAddress);
const payer = input.payer ? new PublicKey(input.payer) : undefined;
const apiResponse = await fetch(
"https://api2.gib.work/tasks/public/transaction",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: input.title,
content: input.content,
requirements: input.requirements,
tags: input.tags,
payer: payer?.toBase58() || agent.wallet.publicKey.toBase58(),
token: {
mintAddress: tokenMintAddress.toBase58(),
amount: input.tokenAmount,
},
}),
}
const responseData = await create_gibwork_task(
agent,
input.title,
input.content,
input.requirements,
input.tags,
new PublicKey(input.tokenMintAddress),
input.tokenAmount,
input.payer ? new PublicKey(input.payer) : undefined
);
if (!apiResponse.ok) {
return {
status: "error",
message: `Failed to create task: ${apiResponse.statusText}`
};
}
const responseData = await apiResponse.json();
if (!responseData.taskId || !responseData.serializedTransaction) {
return {
status: "error",
message: responseData.message || "Invalid response from Gibwork API"
};
}
const serializedTransaction = Buffer.from(
responseData.serializedTransaction,
"base64"
);
const tx = VersionedTransaction.deserialize(serializedTransaction);
tx.sign([agent.wallet]);
const signature = await agent.connection.sendTransaction(tx, {
preflightCommitment: "confirmed",
maxRetries: 3,
});
const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
return {
status: "success",
taskId: responseData.taskId,
signature,
signature: responseData.signature,
message: `Successfully created task: ${input.title}`
};
} catch (error: any) {

View File

@@ -2,6 +2,8 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import OpenAI from "openai";
import { create } from "domain";
import { create_image } from "../tools/create_image";
const createImageAction: Action = {
name: "solana_create_image",
@@ -60,31 +62,17 @@ const createImageAction: Action = {
};
}
const { prompt, model, size, quality, style } = input;
const { prompt, model, size } = input;
const openai = new OpenAI({
apiKey: agent.openai_api_key
});
const response = await openai.images.generate({
prompt,
model,
n: 1,
size,
quality,
style
});
if (!response.data || response.data.length === 0) {
return {
status: "error",
message: "No image was generated"
};
}
const response = await create_image(agent, prompt, model, size);
return {
status: "success",
imageUrl: response.data[0].url,
imageUrl: response.images[0].url,
message: "Successfully generated image"
};
} catch (error: any) {

View File

@@ -4,6 +4,7 @@ import { z } from "zod";
import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { openbookCreateMarket } from "../tools";
const createOpenbookMarketAction: Action = {
name: "solana_create_openbook_market",
@@ -57,54 +58,18 @@ const createOpenbookMarketAction: Action = {
const lotSize = input.lotSize || 1;
const tickSize = input.tickSize || 0.01;
const raydium = await Raydium.load({
owner: agent.wallet,
connection: agent.connection,
});
// Get mint info
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if (!baseMintInfo || !quoteMintInfo) {
return {
status: "error",
message: "Failed to fetch mint information"
};
}
// Verify token program
if (
baseMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) {
return {
status: "error",
message: "Openbook market only supports TOKEN_PROGRAM_ID mints. For token-2022, please use Raydium CPMM pool instead."
};
}
// Create market
const { execute } = await raydium.marketV2.create({
baseInfo: {
mint: baseMint,
decimals: MintLayout.decode(baseMintInfo.data).decimals,
},
quoteInfo: {
mint: quoteMint,
decimals: MintLayout.decode(quoteMintInfo.data).decimals,
},
const signatures = await openbookCreateMarket(
agent,
baseMint,
quoteMint,
lotSize,
tickSize,
dexProgramId: OPEN_BOOK_PROGRAM,
txVersion: TxVersion.V0,
});
const { txIds } = await execute({ sequentially: true });
tickSize
);
return {
status: "success",
signatures: txIds,
signatures,
message: "Successfully created Openbook market"
};
} catch (error: any) {

View File

@@ -4,6 +4,7 @@ import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js";
import { createOrcaSingleSidedWhirlpool } from "../tools";
// Fee tiers mapping from the original tool
const FEE_TIERS = {
@@ -79,7 +80,8 @@ const createOrcaSingleSidedWhirlpoolAction: Action = {
const feeTier = input.feeTier as keyof typeof FEE_TIERS;
// Create the whirlpool
const signature = await agent.createOrcaSingleSidedWhirlpool(
const signature = await createOrcaSingleSidedWhirlpool(
agent,
depositTokenAmount,
depositTokenMint,
otherTokenMint,

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { deploy_collection } from "../tools";
interface CollectionOptions {
name: string;
@@ -64,7 +65,7 @@ const deployCollectionAction: Action = {
royaltyBasisPoints: input.royaltyBasisPoints
};
const result = await agent.deployCollection(options);
const result = await deploy_collection(agent, options);
return {
status: "success",

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action, ActionExample } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { deploy_token } from "../tools";
const deployTokenAction: Action = {
name: "deploy_token",
@@ -55,19 +56,27 @@ const deployTokenAction: Action = {
initialSupply: z.number().optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const result = await agent.deployToken(
input.name,
input.uri,
input.symbol,
input.decimals,
input.initialSupply
);
try {
const result = await deploy_token(
agent,
input.name,
input.uri,
input.symbol,
input.decimals,
input.initialSupply
);
return {
mint: result.mint.toString(),
status: "success",
message: "Token deployed successfully"
};
return {
mint: result.mint.toString(),
status: "success",
message: "Token deployed successfully"
};
} catch (error: any) {
return {
status: "error",
message: `Token deployment failed: ${error.message}`
};
}
}
}

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { fetchPrice } from "../tools";
const fetchPriceAction: Action = {
name: "solana_fetch_price",
@@ -34,24 +35,7 @@ const fetchPriceAction: Action = {
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const tokenId = new PublicKey(input.tokenAddress);
const response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`);
if (!response.ok) {
return {
status: "error",
message: `Failed to fetch price: ${response.statusText}`
};
}
const data = await response.json();
const price = data.data[tokenId.toBase58()]?.price;
if (!price) {
return {
status: "error",
message: "Price data not available for the given token"
};
}
const price = await fetchPrice(tokenId);
return {
status: "success",

View File

@@ -1,6 +1,7 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { getAllDomainsTLDs } from "../tools";
const getAllDomainsTLDsAction: Action = {
name: "solana_get_all_domains_tlds",
@@ -30,7 +31,7 @@ const getAllDomainsTLDsAction: Action = {
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
// Get all domain TLDs
const tlds = await agent.getAllDomainsTLDs();
const tlds = await getAllDomainsTLDs(agent);
return {
status: "success",

View File

@@ -1,6 +1,7 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { getAllRegisteredAllDomains } from "../tools";
const getAllRegisteredAllDomainsAction: Action = {
name: "solana_get_all_registered_all_domains",
@@ -47,14 +48,12 @@ const getAllRegisteredAllDomainsAction: Action = {
const offset = input.offset || 0;
// Get all registered domains
const domains = await agent.getAllRegisteredAllDomains();
const domains = await getAllRegisteredAllDomains(agent);
// Apply pagination
const paginatedDomains = domains.slice(offset, offset + limit);
return {
status: "success",
domains: paginatedDomains,
domains: domains.slice(offset, offset + limit),
total: domains.length,
message: "Successfully retrieved registered domains"
};

View File

@@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { TldParser } from "@onsol/tldparser";
import { getMainAllDomainsDomain } from "../tools";
const getMainAllDomainsDomainAction: Action = {
name: "solana_get_main_all_domains_domain",
@@ -37,11 +38,7 @@ const getMainAllDomainsDomainAction: Action = {
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const address = new PublicKey(input.address);
// Get the main domain using TldParser
const parser = new TldParser(agent.connection);
const mainDomain = await parser.getMainDomain(address);
const mainDomain = await getMainAllDomainsDomain(agent, new PublicKey(input.address));
if (!mainDomain) {
return {
@@ -52,7 +49,7 @@ const getMainAllDomainsDomainAction: Action = {
return {
status: "success",
domain: mainDomain.domain,
domain: mainDomain,
message: "Successfully retrieved main domain"
};
} catch (error: any) {

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { getOwnedAllDomains } from "../tools";
const getOwnedAllDomainsAction: Action = {
name: "solana_get_owned_all_domains",
@@ -40,7 +41,7 @@ const getOwnedAllDomainsAction: Action = {
const address = new PublicKey(input.address);
// Get owned domains
const domains = await agent.getOwnedAllDomains(address);
const domains = await getOwnedAllDomains(agent, address);
return {
status: "success",

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { getOwnedDomainsForTLD } from "../tools";
const getOwnedDomainsForTLDAction: Action = {
name: "solana_get_owned_domains_for_tld",
@@ -40,7 +41,7 @@ const getOwnedDomainsForTLDAction: Action = {
const tld = input.tld.toLowerCase();
// Get owned domains for TLD
const domains = await agent.getOwnedDomainsForTLD(tld);
const domains = await getOwnedDomainsForTLD(agent, tld);
return {
status: "success",

View File

@@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js";
import { getPrimaryDomain } from "../tools";
const getPrimaryDomainAction: Action = {
name: "solana_get_primary_domain",
@@ -39,22 +40,16 @@ const getPrimaryDomainAction: Action = {
try {
const account = new PublicKey(input.account);
const { reverse, stale } = await _getPrimaryDomain(
agent.connection,
const response = await getPrimaryDomain(
agent,
account
);
if (stale) {
return {
status: "error",
message: `Primary domain is stale for account: ${account.toBase58()}`
};
}
return {
status: "success",
domain: reverse,
message: `Primary domain: ${reverse}`
domain: response,
message: `Primary domain: ${response}`
};
} catch (error: any) {
return {

View File

@@ -1,6 +1,7 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { getTPS } from "../tools";
const getTPSAction: Action = {
name: "solana_get_tps",
@@ -28,27 +29,11 @@ const getTPSAction: Action = {
schema: z.object({}), // No input parameters required
handler: async (agent: SolanaAgentKit, _input: Record<string, any>) => {
try {
const perfSamples = await agent.connection.getRecentPerformanceSamples();
if (
!perfSamples.length ||
!perfSamples[0]?.numTransactions ||
!perfSamples[0]?.samplePeriodSecs
) {
return {
status: "error",
message: "No performance samples available"
};
}
const tps = Math.round(
perfSamples[0].numTransactions / perfSamples[0].samplePeriodSecs
);
const response = await getTPS(agent);
return {
status: "success",
tps,
message: `Current network TPS: ${tps}`
response,
message: `Current network TPS: ${response}`
};
} catch (error: any) {
return {

View File

@@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { JupiterTokenData } from "../types";
import { getTokenAddressFromTicker, getTokenDataByAddress } from "../tools";
const getTokenDataAction: Action = {
name: "solana_get_token_data",
@@ -59,59 +60,20 @@ const getTokenDataAction: Action = {
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
let tokenData: JupiterTokenData | undefined;
if (input.address) {
const mint = new PublicKey(input.address);
const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = (await response.json()) as JupiterTokenData[];
tokenData = data.find((token: JupiterTokenData) => token.address === mint.toBase58());
tokenData = await getTokenDataByAddress(new PublicKey(input.address));
} else if (input.ticker) {
const response = await fetch(
`https://api.dexscreener.com/latest/dex/search?q=${input.ticker}`
);
const data = await response.json();
if (!data.pairs || data.pairs.length === 0) {
return {
status: "error",
message: `No token found for ticker: ${input.ticker}`
};
const address = await getTokenAddressFromTicker(input.ticker);
if (address) {
tokenData = await getTokenDataByAddress(new PublicKey(address));
}
let solanaPairs = data.pairs
.filter((pair: any) => pair.chainId === "solana")
.sort((a: any, b: any) => (b.fdv || 0) - (a.fdv || 0))
.filter(
(pair: any) =>
pair.baseToken.symbol.toLowerCase() === input.ticker.toLowerCase()
);
if (solanaPairs.length === 0) {
return {
status: "error",
message: `No Solana token found for ticker: ${input.ticker}`
};
}
const address = solanaPairs[0].baseToken.address;
const jupResponse = await fetch("https://tokens.jup.ag/tokens?tags=verified");
const jupData = (await jupResponse.json()) as JupiterTokenData[];
tokenData = jupData.find((token: JupiterTokenData) => token.address === address);
}
if (!tokenData) {
return {
status: "error",
message: "Token not found or not verified"
};
}
return {
status: "success",
token: {

View File

@@ -16,15 +16,12 @@ import createGibworkTaskAction from "./createGibworkTask";
import resolveSolDomainAction from "./resolveSolDomain";
import pythFetchPriceAction from "./pythFetchPrice";
import getOwnedDomainsForTLDAction from "./getOwnedDomainsForTLD";
import createRaydiumCLMMAction from "./createRaydiumCLMM";
import getPrimaryDomainAction from "./getPrimaryDomain";
import getAllDomainsTLDsAction from "./getAllDomainsTLDs";
import getOwnedAllDomainsAction from "./getOwnedAllDomains";
import createImageAction from "./createImage";
import getMainAllDomainsDomainAction from "./getMainAllDomainsDomain";
import getAllRegisteredAllDomainsAction from "./getAllRegisteredAllDomains";
import createRaydiumCPMMAction from "./createRaydiumCPMM";
import sendCompressedAirdropAction from "./sendCompressedAirdrop";
import raydiumCreateCpmmAction from "./raydiumCreateCpmm";
import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4";
import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool";
@@ -49,15 +46,12 @@ export const actions = [
resolveSolDomainAction,
pythFetchPriceAction,
getOwnedDomainsForTLDAction,
createRaydiumCLMMAction,
getPrimaryDomainAction,
getAllDomainsTLDsAction,
getOwnedAllDomainsAction,
createImageAction,
getMainAllDomainsDomainAction,
getAllRegisteredAllDomainsAction,
createRaydiumCPMMAction,
sendCompressedAirdropAction,
raydiumCreateCpmmAction,
raydiumCreateAmmV4Action,
createOrcaSingleSidedWhirlpoolAction,

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction, Keypair } from "@solana/web3.js";
import { launchPumpFunToken } from "../tools";
const launchPumpfunTokenAction: Action = {
name: "solana_launch_pumpfun_token",
@@ -82,113 +83,14 @@ const launchPumpfunTokenAction: Action = {
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const mintKeypair = Keypair.generate();
// Upload metadata
const formData = new URLSearchParams();
formData.append("name", input.tokenName);
formData.append("symbol", input.tokenTicker);
formData.append("description", input.description);
formData.append("showName", "true");
if (input.twitter) {
formData.append("twitter", input.twitter);
}
if (input.telegram) {
formData.append("telegram", input.telegram);
}
if (input.website) {
formData.append("website", input.website);
}
// Fetch and process image
const imageResponse = await fetch(input.imageUrl);
const imageBlob = await imageResponse.blob();
const imageFile = new File([imageBlob], "token_image.png", { type: "image/png" });
// Create final form data
const finalFormData = new FormData();
for (const [key, value] of formData.entries()) {
finalFormData.append(key, value);
}
finalFormData.append("file", imageFile);
// Upload metadata to IPFS
const metadataResponse = await fetch("https://pump.fun/api/ipfs", {
method: "POST",
body: finalFormData,
});
if (!metadataResponse.ok) {
throw new Error(`Metadata upload failed: ${metadataResponse.statusText}`);
}
const metadataResult = await metadataResponse.json();
// Create token transaction
const payload = {
publicKey: agent.wallet_address.toBase58(),
action: "create",
tokenMetadata: {
name: metadataResult.metadata.name,
symbol: metadataResult.metadata.symbol,
uri: metadataResult.metadataUri,
},
mint: mintKeypair.publicKey.toBase58(),
denominatedInSol: "true",
amount: input.initialLiquiditySOL || 0.0001,
slippage: input.slippageBps || 5,
priorityFee: input.priorityFee || 0.00005,
pool: "pump",
};
const txResponse = await fetch("https://pumpportal.fun/api/trade-local", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!txResponse.ok) {
const errorText = await txResponse.text();
throw new Error(`Transaction creation failed: ${txResponse.status} - ${errorText}`);
}
// Process and sign transaction
const transactionData = await txResponse.arrayBuffer();
const tx = VersionedTransaction.deserialize(new Uint8Array(transactionData));
// Get latest blockhash
const { blockhash, lastValidBlockHeight } = await agent.connection.getLatestBlockhash();
tx.message.recentBlockhash = blockhash;
// Sign transaction
tx.sign([mintKeypair, agent.wallet]);
// Send transaction
const signature = await agent.connection.sendTransaction(tx, {
skipPreflight: false,
preflightCommitment: "confirmed",
maxRetries: 5,
});
// Wait for confirmation
const confirmation = await agent.connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight,
});
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${confirmation.value.err}`);
}
const { tokenName, tokenTicker, description, imageUrl } = input;
const result = await launchPumpFunToken(agent, tokenName, tokenTicker, description, imageUrl, input);
return {
status: "success",
signature,
mint: mintKeypair.publicKey.toBase58(),
metadataUri: metadataResult.metadataUri,
signature: result.signature,
mint: result.mint,
metadataUri: result.metadataUri,
message: "Successfully launched token on Pump.fun"
};
} catch (error: any) {

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction } from "@solana/web3.js";
import { lendAsset } from "../tools";
const lendAssetAction: Action = {
name: "solana_lend_asset",
@@ -38,55 +39,11 @@ const lendAssetAction: Action = {
try {
const amount = input.amount as number;
const response = await fetch(
`https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
}),
}
);
if (!response.ok) {
return {
status: "error",
message: `Failed to get lending transaction: ${response.statusText}`
};
}
const data = await response.json();
// Deserialize the transaction
const luloTxn = VersionedTransaction.deserialize(
Buffer.from(data.transaction, "base64")
);
// Get a recent blockhash and set it
const { blockhash } = await agent.connection.getLatestBlockhash();
luloTxn.message.recentBlockhash = blockhash;
// Sign and send transaction
luloTxn.sign([agent.wallet]);
const signature = await agent.connection.sendTransaction(luloTxn, {
preflightCommitment: "confirmed",
maxRetries: 3,
});
// Wait for confirmation
const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
const response = await lendAsset(agent, amount);
return {
status: "success",
signature,
signature: response,
message: `Successfully lent ${amount} USDC`
};
} catch (error: any) {

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { mintCollectionNFT } from "../tools";
const mintNFTAction: Action = {
name: "solana_mint_nft",
@@ -63,13 +64,14 @@ const mintNFTAction: Action = {
recipient: z.string().min(32, "Invalid recipient address").optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const result = await agent.mintNFT(
const result = await mintCollectionNFT(
agent,
new PublicKey(input.collectionMint),
{
name: input.name,
uri: input.uri,
uri: input.uri
},
input.recipient ? new PublicKey(input.recipient) : agent.wallet_address
input.recipient ? new PublicKey(input.recipient) : undefined
);
return {

View File

@@ -3,6 +3,7 @@ import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import BN from "bn.js";
import { pythFetchPrice } from "../tools";
const pythFetchPriceAction: Action = {
name: "solana_pyth_fetch_price",
@@ -39,27 +40,7 @@ const pythFetchPriceAction: Action = {
try {
const priceFeedId = input.priceFeedId as string;
// Connect to Hermes service
const stableHermesServiceUrl = "https://hermes.pyth.network";
const connection = new PriceServiceConnection(stableHermesServiceUrl);
const feeds = [priceFeedId];
const currentPrice = await connection.getLatestPriceFeeds(feeds);
if (!currentPrice || currentPrice.length === 0) {
return {
status: "error",
message: "Price data not available for the given feed ID"
};
}
// Get price and exponent from price feed
const price = new BN(currentPrice[0].getPriceUnchecked().price);
const exponent = new BN(currentPrice[0].getPriceUnchecked().expo);
// Convert to scaled price
const scaledPrice = price.div(new BN(10).pow(exponent));
const priceStr = scaledPrice.toString();
const priceStr = await pythFetchPrice(priceFeedId);
return {
status: "success",

View File

@@ -12,6 +12,7 @@ import {
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import { raydiumCreateAmmV4 } from "../tools";
const raydiumCreateAmmV4Action: Action = {
name: "solana_raydium_create_amm_v4",
@@ -72,70 +73,7 @@ const raydiumCreateAmmV4Action: Action = {
const quoteAmount = new BN(input.quoteAmount);
const startTime = new BN(input.startTime);
const raydium = await Raydium.load({
owner: agent.wallet,
connection: agent.connection,
});
const marketBufferInfo = await agent.connection.getAccountInfo(
new PublicKey(marketId),
);
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(
marketBufferInfo!.data,
);
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if (
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) {
throw new Error(
"amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead",
);
}
if (
baseAmount
.mul(quoteAmount)
.lte(
new BN(1)
.mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals))
.pow(new BN(2)),
)
) {
throw new Error(
"initial liquidity too low, try adding more baseAmount/quoteAmount",
);
}
const { execute } = await raydium.liquidity.createPoolV4({
programId: AMM_V4,
marketInfo: {
marketId,
programId: OPEN_BOOK_PROGRAM,
},
baseMintInfo: {
mint: baseMint,
decimals: MintLayout.decode(baseMintInfo.data).decimals,
},
quoteMintInfo: {
mint: quoteMint,
decimals: MintLayout.decode(quoteMintInfo.data).decimals,
},
baseAmount,
quoteAmount,
startTime,
ownerInfo: {
useSOLBalance: true,
},
associatedOnly: false,
txVersion: TxVersion.V0,
feeDestinationId: FEE_DESTINATION_ID,
});
const { txId } = await execute({ sendAndConfirm: true });
const txId = await raydiumCreateAmmV4(agent, marketId, baseAmount, quoteAmount, startTime);
return {
status: "success",

View File

@@ -10,6 +10,7 @@ import {
import { MintLayout } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import { raydiumCreateCpmm } from "../tools";
const raydiumCreateCpmmAction: Action = {
name: "solana_raydium_create_cpmm",
@@ -68,62 +69,7 @@ const raydiumCreateCpmmAction: Action = {
const mintBAmount = new BN(input.quoteAmount);
const startTime = new BN(input.startTime);
const raydium = await Raydium.load({
owner: agent.wallet,
connection: agent.connection,
});
const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo(
[mintA, mintB],
);
if (mintInfoA === null || mintInfoB === null) {
throw Error("fetch mint info error");
}
const mintDecodeInfoA = MintLayout.decode(mintInfoA.data);
const mintDecodeInfoB = MintLayout.decode(mintInfoB.data);
const mintFormatInfoA = {
chainId: 101,
address: mintA.toString(),
programId: mintInfoA.owner.toString(),
logoURI: "",
symbol: "",
name: "",
decimals: mintDecodeInfoA.decimals,
tags: [],
extensions: {},
};
const mintFormatInfoB = {
chainId: 101,
address: mintB.toString(),
programId: mintInfoB.owner.toString(),
logoURI: "",
symbol: "",
name: "",
decimals: mintDecodeInfoB.decimals,
tags: [],
extensions: {},
};
const { execute } = await raydium.cpmm.createPool({
programId: CREATE_CPMM_POOL_PROGRAM,
poolFeeAccount: CREATE_CPMM_POOL_FEE_ACC,
mintA: mintFormatInfoA,
mintB: mintFormatInfoB,
mintAAmount,
mintBAmount,
startTime,
//@ts-expect-error sdk bug
feeConfig: { id: configId.toString() },
associatedOnly: false,
ownerInfo: {
useSOLBalance: true,
},
txVersion: TxVersion.V0,
});
const { txId } = await execute({ sendAndConfirm: true });
const txId = await raydiumCreateCpmm(agent, mintA, mintB, configId, mintAAmount, mintBAmount, startTime);
return {
status: "success",

View File

@@ -5,6 +5,7 @@ import { Transaction } from "@solana/web3.js";
import { registerDomainNameV2 } from "@bonfida/spl-name-service";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { TOKENS } from "../constants";
import { registerDomain } from "../tools";
const registerDomainAction: Action = {
name: "solana_register_domain",
@@ -48,50 +49,7 @@ const registerDomainAction: Action = {
const name = input.name as string;
const spaceKB = (input.spaceKB as number) || 1;
// Validate space size
if (spaceKB > 10) {
return {
status: "error",
message: "Maximum domain size is 10KB"
};
}
// Convert KB to bytes
const space = spaceKB * 1_000;
const buyerTokenAccount = await getAssociatedTokenAddressSync(
agent.wallet_address,
TOKENS.USDC
);
// Create registration instruction
const instruction = await registerDomainNameV2(
agent.connection,
name,
space,
agent.wallet_address,
buyerTokenAccount
);
// Create and sign transaction
const transaction = new Transaction().add(...instruction);
transaction.recentBlockhash = (
await agent.connection.getLatestBlockhash()
).blockhash;
transaction.feePayer = agent.wallet_address;
// Sign and send transaction
const signature = await agent.connection.sendTransaction(transaction, [
agent.wallet
]);
// Wait for confirmation
const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
const signature = await registerDomain(agent, name, spaceKB);
return {
status: "success",

View File

@@ -1,6 +1,7 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { request_faucet_funds } from "../tools";
const requestFundsAction: Action = {
name: "solana_request_funds",
@@ -27,7 +28,7 @@ const requestFundsAction: Action = {
],
schema: z.object({}), // No input parameters required
handler: async (agent: SolanaAgentKit, _input: Record<string, any>) => {
await agent.requestFaucetFunds();
await request_faucet_funds(agent);
return {
status: "success",

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { TldParser } from "@onsol/tldparser";
import { resolveAllDomains } from "../tools";
const resolveDomainAction: Action = {
name: "solana_resolve_domain",
@@ -33,20 +34,10 @@ const resolveDomainAction: Action = {
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const domain = input.domain as string;
const tld = await new TldParser(agent.connection).getOwnerFromDomainTld(
domain
);
if (!tld) {
return {
status: "error",
message: "Domain not found"
};
}
const tld = await resolveAllDomains(agent, domain);
return {
status: "success",
owner: tld.toBase58(),
owner: tld,
message: `Successfully resolved domain ${domain}`
};
} catch (error: any) {

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { resolve } from "@bonfida/spl-name-service";
import { resolveSolDomain } from "../tools";
const resolveSolDomainAction: Action = {
name: "solana_resolve_sol_domain",
@@ -38,24 +39,12 @@ const resolveSolDomainAction: Action = {
try {
const domain = input.domain as string;
if (!domain || typeof domain !== "string") {
return {
status: "error",
message: "Invalid domain. Expected a non-empty string."
};
}
// Remove .sol suffix if present for consistent handling
const cleanDomain = domain.toLowerCase().endsWith(".sol")
? domain.slice(0, -4)
: domain;
const ownerAddress = await resolve(agent.connection, cleanDomain);
const res = await resolveSolDomain(agent,domain)
return {
status: "success",
owner: ownerAddress.toBase58(),
message: `Successfully resolved ${cleanDomain}.sol`
owner: res.toString(),
message: `Successfully resolved ${res}`
};
} catch (error: any) {
return {

View File

@@ -2,6 +2,7 @@ import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction } from "@solana/web3.js";
import { stakeWithJup, trade } from "../tools";
const stakeWithJupAction: Action = {
name: "solana_stake_with_jup",
@@ -38,55 +39,10 @@ const stakeWithJupAction: Action = {
try {
const amount = input.amount as number;
// Get staking transaction from Jupiter
const res = await fetch(
`https://worker.jup.ag/blinks/swap/So11111111111111111111111111111111111111112/jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v/${amount}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
}),
}
);
if (!res.ok) {
return {
status: "error",
message: `Failed to get staking transaction: ${res.statusText}`
};
}
const data = await res.json();
// Deserialize and prepare transaction
const txn = VersionedTransaction.deserialize(
Buffer.from(data.transaction, "base64")
);
const { blockhash } = await agent.connection.getLatestBlockhash();
txn.message.recentBlockhash = blockhash;
// Sign and send transaction
txn.sign([agent.wallet]);
const signature = await agent.connection.sendTransaction(txn, {
preflightCommitment: "confirmed",
maxRetries: 3,
});
// Confirm transaction
const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
const res = await stakeWithJup(agent,amount)
return {
status: "success",
signature,
res,
message: `Successfully staked ${amount} SOL for jupSOL`
};
} catch (error: any) {

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { trade } from "../tools";
const tradeAction: Action = {
name: "solana_trade",
@@ -58,7 +59,7 @@ const tradeAction: Action = {
slippageBps: z.number().min(0).max(10000).optional()
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
const tx = await agent.trade(
const tx = await trade(agent,
new PublicKey(input.outputMint),
input.inputAmount,
input.inputMint

View File

@@ -2,6 +2,7 @@ import { PublicKey } from "@solana/web3.js";
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { transfer } from "../tools";
const transferAction: Action = {
name: "solana_transfer",
@@ -59,7 +60,7 @@ const transferAction: Action = {
const recipient = new PublicKey(input.to);
const mintAddress = input.mint ? new PublicKey(input.mint) : undefined;
const tx = await agent.transfer(recipient, input.amount, mintAddress);
const tx = await transfer(agent,recipient, input.amount, mintAddress);
return {
status: "success",