adding missing tools

This commit is contained in:
Fahri Bilici
2024-12-27 23:34:15 +01:00
parent 9297d78838
commit 9326da25b1
23 changed files with 2180 additions and 1 deletions

View File

@@ -0,0 +1,135 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
const createGibworkTaskAction: Action = {
name: "solana_create_gibwork_task",
similes: [
"create task",
"post job",
"create gig",
"post task",
"create work",
"new task on gibwork"
],
description: "Create a new task on the Gibwork platform with payment in SPL tokens",
examples: [
[
{
input: {
title: "Build a Solana dApp",
content: "Create a simple Solana dApp with React frontend",
requirements: "Experience with Rust and React",
tags: ["solana", "rust", "react"],
tokenMintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
tokenAmount: 100
},
output: {
status: "success",
taskId: "task_123",
signature: "3YKpM1...",
message: "Successfully created task: Build a Solana dApp"
},
explanation: "Create a new task on Gibwork with 100 USDC payment"
}
]
],
schema: z.object({
title: z.string()
.min(1)
.describe("Title of the task"),
content: z.string()
.min(1)
.describe("Description of the task"),
requirements: z.string()
.min(1)
.describe("Requirements to complete the task"),
tags: z.array(z.string())
.min(1)
.describe("List of tags associated with the task"),
tokenMintAddress: z.string()
.describe("Token mint address for payment"),
tokenAmount: z.number()
.positive()
.describe("Payment amount for the task"),
payer: z.string()
.optional()
.describe("Optional payer address (defaults to wallet address)")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
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,
},
}),
}
);
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,
message: `Successfully created task: ${input.title}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create task: ${error.message}`
};
}
}
};
export default createGibworkTaskAction;

114
src/actions/createImage.ts Normal file
View File

@@ -0,0 +1,114 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import OpenAI from "openai";
const createImageAction: Action = {
name: "solana_create_image",
similes: [
"generate image",
"create artwork",
"make image",
"generate artwork",
"create picture",
"generate picture"
],
description: "Create an AI-generated image based on a text prompt using OpenAI's DALL-E models",
examples: [
[
{
input: {
prompt: "A beautiful sunset over a mountain landscape",
model: "dall-e-3",
size: "1024x1024",
quality: "standard",
style: "natural"
},
output: {
status: "success",
imageUrl: "https://example.com/image.png",
message: "Successfully generated image"
},
explanation: "Generate an image of a sunset landscape using DALL-E 3"
}
]
],
schema: z.object({
prompt: z.string()
.min(1)
.max(1000)
.describe("The text description of the image to generate"),
model: z.enum(["dall-e-3"])
.default("dall-e-3")
.describe("The AI model to use for generation"),
size: z.enum(["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"])
.default("1024x1024")
.describe("The size of the generated image"),
quality: z.enum(["standard", "hd"])
.default("standard")
.describe("The quality level of the generated image"),
style: z.enum(["natural", "vivid"])
.default("natural")
.describe("The style of the generated image")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
if (!agent.openai_api_key) {
return {
status: "error",
message: "OpenAI API key not found in agent configuration"
};
}
const { prompt, model, size, quality, style } = 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"
};
}
return {
status: "success",
imageUrl: response.data[0].url,
message: "Successfully generated image"
};
} catch (error: any) {
// Handle specific OpenAI error types
if (error.response) {
const { status, data } = error.response;
if (status === 429) {
return {
status: "error",
message: "Rate limit exceeded. Please try again later."
};
}
return {
status: "error",
message: `OpenAI API error: ${data.error?.message || error.message}`
};
}
return {
status: "error",
message: `Failed to generate image: ${error.message}`
};
}
}
};
export default createImageAction;

View File

@@ -0,0 +1,119 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
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";
const createOpenbookMarketAction: Action = {
name: "solana_create_openbook_market",
similes: [
"create openbook market",
"setup trading market",
"new openbook market",
"create trading pair",
"setup dex market",
"new trading market"
],
description: "Create a new trading market on Openbook DEX",
examples: [
[
{
input: {
baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
quoteMint: "So11111111111111111111111111111111111111112", // SOL
lotSize: 1,
tickSize: 0.01
},
output: {
status: "success",
signatures: ["2ZE7Rz...", "3YKpM1..."],
message: "Successfully created Openbook market"
},
explanation: "Create a new USDC/SOL market on Openbook with default lot and tick sizes"
}
]
],
schema: z.object({
baseMint: z.string()
.min(1)
.describe("The base token's mint address"),
quoteMint: z.string()
.min(1)
.describe("The quote token's mint address"),
lotSize: z.number()
.positive()
.default(1)
.describe("The minimum order size (lot size)"),
tickSize: z.number()
.positive()
.default(0.01)
.describe("The minimum price increment (tick size)")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const baseMint = new PublicKey(input.baseMint);
const quoteMint = new PublicKey(input.quoteMint);
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,
},
lotSize,
tickSize,
dexProgramId: OPEN_BOOK_PROGRAM,
txVersion: TxVersion.V0,
});
const { txIds } = await execute({ sequentially: true });
return {
status: "success",
signatures: txIds,
message: "Successfully created Openbook market"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create Openbook market: ${error.message}`
};
}
}
};
export default createOpenbookMarketAction;

View File

@@ -0,0 +1,105 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { BN } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js";
// Fee tiers mapping from the original tool
const FEE_TIERS = {
0.01: 1,
0.02: 2,
0.04: 4,
0.05: 8,
0.16: 16,
0.3: 64,
0.65: 96,
1.0: 128,
2.0: 256,
} as const;
const createOrcaSingleSidedWhirlpoolAction: Action = {
name: "solana_create_orca_single_sided_whirlpool",
similes: [
"create orca whirlpool",
"setup orca single sided pool",
"initialize orca whirlpool",
"create orca concentrated pool",
"setup orca concentrated liquidity",
"create orca trading pair"
],
description: "Create a new single-sided whirlpool on Orca with concentrated liquidity",
examples: [
[
{
input: {
depositTokenAmount: "1000000000000", // 1 million tokens with 6 decimals
depositTokenMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
otherTokenMint: "So11111111111111111111111111111111111111112", // SOL
initialPrice: "0.001",
maxPrice: "5.0",
feeTier: 0.3
},
output: {
status: "success",
signature: "2ZE7Rz...",
message: "Successfully created Orca single-sided whirlpool"
},
explanation: "Create a USDC/SOL whirlpool with 1M USDC initial liquidity"
}
]
],
schema: z.object({
depositTokenAmount: z.string()
.min(1)
.describe("The amount of deposit token to provide as liquidity (including decimals)"),
depositTokenMint: z.string()
.min(1)
.describe("The mint address of the token being deposited"),
otherTokenMint: z.string()
.min(1)
.describe("The mint address of the other token in the pool"),
initialPrice: z.string()
.min(1)
.describe("Initial price of deposit token in terms of the other token"),
maxPrice: z.string()
.min(1)
.describe("Maximum price at which liquidity is added"),
feeTier: z.number()
.refine((val) => val in FEE_TIERS, "Invalid fee tier")
.describe("Fee tier percentage for the pool (e.g., 0.3 for 0.3%)")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const depositTokenAmount = new BN(input.depositTokenAmount);
const depositTokenMint = new PublicKey(input.depositTokenMint);
const otherTokenMint = new PublicKey(input.otherTokenMint);
const initialPrice = new Decimal(input.initialPrice);
const maxPrice = new Decimal(input.maxPrice);
const feeTier = input.feeTier as keyof typeof FEE_TIERS;
// Create the whirlpool
const signature = await agent.createOrcaSingleSidedWhirlpool(
depositTokenAmount,
depositTokenMint,
otherTokenMint,
initialPrice,
maxPrice,
feeTier
);
return {
status: "success",
signature,
message: "Successfully created Orca single-sided whirlpool"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create whirlpool: ${error.message}`
};
}
}
};
export default createOrcaSingleSidedWhirlpoolAction;

70
src/actions/fetchPrice.ts Normal file
View File

@@ -0,0 +1,70 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
const fetchPriceAction: Action = {
name: "solana_fetch_price",
similes: [
"get token price",
"check price",
"token value",
"price check",
"get price in usd"
],
description: "Fetch the current price of a Solana token in USDC using Jupiter API",
examples: [
[
{
input: {
tokenAddress: "So11111111111111111111111111111111111111112"
},
output: {
status: "success",
price: "23.45",
message: "Current price: $23.45 USDC"
},
explanation: "Get the current price of SOL token in USDC"
}
]
],
schema: z.object({
tokenAddress: z.string().describe("The mint address of the token to fetch the price for")
}),
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"
};
}
return {
status: "success",
price,
message: `Current price: $${price} USDC`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to fetch price: ${error.message}`
};
}
}
};
export default fetchPriceAction;

View File

@@ -0,0 +1,49 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const getAllDomainsTLDsAction: Action = {
name: "solana_get_all_domains_tlds",
similes: [
"list domain tlds",
"get domain extensions",
"fetch domain tlds",
"get top level domains",
"list available tlds",
"get domain suffixes"
],
description: "Get a list of all available top-level domains (TLDs) for Solana domains",
examples: [
[
{
input: {},
output: {
status: "success",
tlds: [".sol", ".abc", ".backpack", ".bonk"],
message: "Successfully retrieved all domain TLDs"
},
explanation: "Get a list of all available TLDs that can be used for Solana domains"
}
]
],
schema: z.object({}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
// Get all domain TLDs
const tlds = await agent.getAllDomainsTLDs();
return {
status: "success",
tlds,
message: "Successfully retrieved all domain TLDs"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get domain TLDs: ${error.message}`
};
}
}
};
export default getAllDomainsTLDsAction;

View File

@@ -0,0 +1,70 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const getAllRegisteredAllDomainsAction: Action = {
name: "solana_get_all_registered_all_domains",
similes: [
"list registered domains",
"get all domains",
"fetch registered domains",
"get domain list",
"list active domains",
"get registered names"
],
description: "Get a list of all registered domains across all TLDs",
examples: [
[
{
input: {
limit: 100,
offset: 0
},
output: {
status: "success",
domains: ["solana.sol", "bonk.abc", "wallet.backpack"],
total: 3,
message: "Successfully retrieved registered domains"
},
explanation: "Get the first 100 registered domains across all TLDs"
}
]
],
schema: z.object({
limit: z.number()
.positive()
.max(1000)
.default(100)
.describe("Maximum number of domains to return"),
offset: z.number()
.nonnegative()
.default(0)
.describe("Number of domains to skip")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const limit = input.limit || 100;
const offset = input.offset || 0;
// Get all registered domains
const domains = await agent.getAllRegisteredAllDomains();
// Apply pagination
const paginatedDomains = domains.slice(offset, offset + limit);
return {
status: "success",
domains: paginatedDomains,
total: domains.length,
message: "Successfully retrieved registered domains"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get registered domains: ${error.message}`
};
}
}
};
export default getAllRegisteredAllDomainsAction;

View File

@@ -0,0 +1,67 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { TldParser } from "@onsol/tldparser";
const getMainAllDomainsDomainAction: Action = {
name: "solana_get_main_all_domains_domain",
similes: [
"get main domain",
"fetch primary domain",
"get default domain",
"get main address name",
"get primary name",
"get main domain name"
],
description: "Get the main domain associated with a wallet address",
examples: [
[
{
input: {
address: "7nxQB..."
},
output: {
status: "success",
domain: "solana.sol",
message: "Successfully retrieved main domain"
},
explanation: "Get the main domain name for a given wallet address"
}
]
],
schema: z.object({
address: z.string()
.min(1)
.describe("The wallet address to get the main domain for")
}),
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);
if (!mainDomain) {
return {
status: "error",
message: "No main domain found for this address"
};
}
return {
status: "success",
domain: mainDomain.domain,
message: "Successfully retrieved main domain"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get main domain: ${error.message}`
};
}
}
};
export default getMainAllDomainsDomainAction;

View File

@@ -0,0 +1,60 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
const getOwnedAllDomainsAction: Action = {
name: "solana_get_owned_all_domains",
similes: [
"list owned domains",
"get my domains",
"fetch wallet domains",
"get owned names",
"list my domains",
"get address domains"
],
description: "Get all domains owned by a specific wallet address across all TLDs",
examples: [
[
{
input: {
address: "7nxQB..."
},
output: {
status: "success",
domains: ["solana.sol", "wallet.abc", "user.backpack"],
total: 3,
message: "Successfully retrieved owned domains"
},
explanation: "Get all domain names owned by a specific wallet address"
}
]
],
schema: z.object({
address: z.string()
.min(1)
.describe("The wallet address to get owned domains for")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const address = new PublicKey(input.address);
// Get owned domains
const domains = await agent.getOwnedAllDomains(address);
return {
status: "success",
domains,
total: domains.length,
message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? '' : 's'}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get owned domains: ${error.message}`
};
}
}
};
export default getOwnedAllDomainsAction;

View File

@@ -0,0 +1,60 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
const getOwnedDomainsForTLDAction: Action = {
name: "solana_get_owned_domains_for_tld",
similes: [
"list owned domains for tld",
"get my domains for extension",
"fetch wallet domains by tld",
"get owned names by extension",
"list my domains by tld",
"get address domains for tld"
],
description: "Get all domains owned by a specific wallet address for a given top-level domain (TLD)",
examples: [
[
{
input: {
tld: "sol"
},
output: {
status: "success",
domains: ["solana.sol", "wallet.sol", "user.sol"],
total: 3,
message: "Successfully retrieved owned domains for .sol"
},
explanation: "Get all .sol domain names owned by a specific wallet address"
}
]
],
schema: z.object({
tld: z.string()
.min(1)
.describe("The top-level domain to filter by (e.g., 'sol', 'abc')")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const tld = input.tld.toLowerCase();
// Get owned domains for TLD
const domains = await agent.getOwnedDomainsForTLD(tld);
return {
status: "success",
domains,
total: domains.length,
message: `Successfully retrieved ${domains.length} owned domain${domains.length === 1 ? '' : 's'} for .${tld}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get owned domains: ${error.message}`
};
}
}
};
export default getOwnedDomainsForTLDAction;

View File

@@ -0,0 +1,68 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js";
const getPrimaryDomainAction: Action = {
name: "solana_get_primary_domain",
similes: [
"get primary domain",
"lookup primary domain",
"check primary domain",
"find primary domain",
"get main domain",
"primary sol domain"
],
description: "Get the primary .sol domain associated with a Solana wallet address",
examples: [
[
{
input: {
account: "7nxQB..."
},
output: {
status: "success",
domain: "vitalik.sol",
message: "Primary domain: vitalik.sol"
},
explanation: "Get the primary .sol domain for a wallet address"
}
]
],
schema: z.object({
account: z.string()
.min(1)
.describe("The Solana wallet address to lookup")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const account = new PublicKey(input.account);
const { reverse, stale } = await _getPrimaryDomain(
agent.connection,
account
);
if (stale) {
return {
status: "error",
message: `Primary domain is stale for account: ${account.toBase58()}`
};
}
return {
status: "success",
domain: reverse,
message: `Primary domain: ${reverse}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get primary domain: ${error.message}`
};
}
}
};
export default getPrimaryDomainAction;

62
src/actions/getTPS.ts Normal file
View File

@@ -0,0 +1,62 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
const getTPSAction: Action = {
name: "solana_get_tps",
similes: [
"get transactions per second",
"check network speed",
"network performance",
"transaction throughput",
"network tps"
],
description: "Get the current transactions per second (TPS) of the Solana network",
examples: [
[
{
input: {},
output: {
status: "success",
tps: 3500,
message: "Current network TPS: 3500"
},
explanation: "Get the current TPS of the Solana network"
}
]
],
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
);
return {
status: "success",
tps,
message: `Current network TPS: ${tps}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get TPS: ${error.message}`
};
}
}
};
export default getTPSAction;

134
src/actions/getTokenData.ts Normal file
View File

@@ -0,0 +1,134 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { JupiterTokenData } from "../types";
const getTokenDataAction: Action = {
name: "solana_get_token_data",
similes: [
"get token info",
"token details",
"lookup token",
"find token",
"token data"
],
description: "Get token data from either a token address or ticker symbol",
examples: [
[
{
input: {
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
output: {
status: "success",
token: {
name: "USD Coin",
symbol: "USDC",
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
decimals: 6
}
},
explanation: "Get token data using the token's address"
}
],
[
{
input: {
ticker: "SOL"
},
output: {
status: "success",
token: {
name: "Wrapped SOL",
symbol: "SOL",
address: "So11111111111111111111111111111111111111112",
decimals: 9
}
},
explanation: "Get token data using the token's ticker symbol"
}
]
],
schema: z.object({
address: z.string().optional().describe("The token's mint address"),
ticker: z.string().optional().describe("The token's ticker symbol")
}).refine(data => data.address || data.ticker, {
message: "Either address or ticker must be provided"
}),
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());
} 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}`
};
}
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: {
name: tokenData.name,
symbol: tokenData.symbol,
address: tokenData.address,
decimals: tokenData.decimals,
logoURI: tokenData.logoURI
}
};
} catch (error: any) {
return {
status: "error",
message: `Failed to get token data: ${error.message}`
};
}
}
};
export default getTokenDataAction;

View File

@@ -5,6 +5,30 @@ import deployCollectionAction from "./deployCollection";
import mintNFTAction from "./mintNFT";
import tradeAction from "./trade";
import requestFundsAction from "./requestFunds";
import resolveDomainAction from "./resolveDomain";
import getTokenDataAction from "./getTokenData";
import getTPSAction from "./getTPS";
import fetchPriceAction from "./fetchPrice";
import stakeWithJupAction from "./stakeWithJup";
import registerDomainAction from "./registerDomain";
import lendAssetAction from "./lendAsset";
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";
import launchPumpfunTokenAction from "./launchPumpfunToken";
export const actions = [
deployTokenAction,
@@ -14,7 +38,30 @@ export const actions = [
mintNFTAction,
tradeAction,
requestFundsAction,
// Add more actions here as they are implemented
resolveDomainAction,
getTokenDataAction,
getTPSAction,
fetchPriceAction,
stakeWithJupAction,
registerDomainAction,
lendAssetAction,
createGibworkTaskAction,
resolveSolDomainAction,
pythFetchPriceAction,
getOwnedDomainsForTLDAction,
createRaydiumCLMMAction,
getPrimaryDomainAction,
getAllDomainsTLDsAction,
getOwnedAllDomainsAction,
createImageAction,
getMainAllDomainsDomainAction,
getAllRegisteredAllDomainsAction,
createRaydiumCPMMAction,
sendCompressedAirdropAction,
raydiumCreateCpmmAction,
raydiumCreateAmmV4Action,
createOrcaSingleSidedWhirlpoolAction,
launchPumpfunTokenAction,
];
export type { Action, ActionExample, Handler } from "../types/action";

View File

@@ -0,0 +1,203 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction, Keypair } from "@solana/web3.js";
const launchPumpfunTokenAction: Action = {
name: "solana_launch_pumpfun_token",
similes: [
"create pumpfun token",
"launch token on pumpfun",
"deploy pumpfun token",
"create meme token",
"launch memecoin",
"create pump token"
],
description: "Launch a new token on Pump.fun with customizable metadata and initial liquidity",
examples: [
[
{
input: {
tokenName: "Sample Token",
tokenTicker: "SMPL",
description: "A sample token for demonstration",
imageUrl: "https://example.com/token.png",
twitter: "@sampletoken",
telegram: "t.me/sampletoken",
website: "https://sampletoken.com",
initialLiquiditySOL: 0.1,
slippageBps: 10,
priorityFee: 0.0001
},
output: {
status: "success",
signature: "2ZE7Rz...",
mint: "7nxQB...",
metadataUri: "https://arweave.net/...",
message: "Successfully launched token on Pump.fun"
},
explanation: "Launch a new token with custom metadata and 0.1 SOL initial liquidity"
}
]
],
schema: z.object({
tokenName: z.string()
.min(1)
.max(32)
.describe("Name of the token"),
tokenTicker: z.string()
.min(2)
.max(10)
.describe("Ticker symbol of the token"),
description: z.string()
.min(1)
.max(1000)
.describe("Description of the token"),
imageUrl: z.string()
.url()
.describe("URL of the token image"),
twitter: z.string()
.optional()
.describe("Twitter handle (optional)"),
telegram: z.string()
.optional()
.describe("Telegram group link (optional)"),
website: z.string()
.url()
.optional()
.describe("Website URL (optional)"),
initialLiquiditySOL: z.number()
.min(0.0001)
.default(0.0001)
.describe("Initial liquidity in SOL"),
slippageBps: z.number()
.min(1)
.max(1000)
.default(5)
.describe("Slippage tolerance in basis points"),
priorityFee: z.number()
.min(0.00001)
.default(0.00005)
.describe("Priority fee in SOL")
}),
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}`);
}
return {
status: "success",
signature,
mint: mintKeypair.publicKey.toBase58(),
metadataUri: metadataResult.metadataUri,
message: "Successfully launched token on Pump.fun"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to launch token: ${error.message}`
};
}
}
};
export default launchPumpfunTokenAction;

101
src/actions/lendAsset.ts Normal file
View File

@@ -0,0 +1,101 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction } from "@solana/web3.js";
const lendAssetAction: Action = {
name: "solana_lend_asset",
similes: [
"lend usdc",
"deposit for yield",
"earn yield",
"lend with lulo",
"deposit usdc",
"lending"
],
description: "Lend USDC tokens to earn yield using Lulo protocol",
examples: [
[
{
input: {
amount: 100
},
output: {
status: "success",
signature: "4xKpN2...",
message: "Successfully lent 100 USDC"
},
explanation: "Lend 100 USDC to earn yield on Lulo"
}
]
],
schema: z.object({
amount: z.number()
.positive()
.describe("Amount of USDC to lend")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
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,
});
return {
status: "success",
signature,
message: `Successfully lent ${amount} USDC`
};
} catch (error: any) {
return {
status: "error",
message: `Lending failed: ${error.message}`
};
}
}
};
export default lendAssetAction;

View File

@@ -0,0 +1,78 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import BN from "bn.js";
const pythFetchPriceAction: Action = {
name: "solana_pyth_fetch_price",
similes: [
"get pyth price",
"check pyth price",
"pyth oracle price",
"fetch from pyth",
"pyth price feed",
"oracle price"
],
description: "Fetch the current price from a Pyth oracle price feed",
examples: [
[
{
input: {
priceFeedId: "Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD" // SOL/USD price feed
},
output: {
status: "success",
price: "23.45",
message: "Current price: $23.45"
},
explanation: "Get the current SOL/USD price from Pyth oracle"
}
]
],
schema: z.object({
priceFeedId: z.string()
.min(1)
.describe("The Pyth price feed ID to fetch the price from")
}),
handler: async (_agent: SolanaAgentKit, input: Record<string, any>) => {
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();
return {
status: "success",
price: priceStr,
message: `Current price: $${priceStr}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to fetch price from Pyth: ${error.message}`
};
}
}
};
export default pythFetchPriceAction;

View File

@@ -0,0 +1,154 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import {
AMM_V4,
FEE_DESTINATION_ID,
MARKET_STATE_LAYOUT_V3,
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 BN from "bn.js";
const raydiumCreateAmmV4Action: Action = {
name: "solana_raydium_create_amm_v4",
similes: [
"create raydium v4 pool",
"setup raydium v4 liquidity pool",
"initialize raydium v4 amm",
"create raydium v4 market maker",
"setup raydium v4 pool",
"create raydium v4 trading pair"
],
description: "Create a new AMM V4 pool on Raydium with advanced features and improved efficiency",
examples: [
[
{
input: {
baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
quoteMint: "So11111111111111111111111111111111111111112", // SOL
baseAmount: 1000,
quoteAmount: 10,
startPrice: 100, // 1 SOL = 100 USDC
openTime: 1672531200 // Unix timestamp
},
output: {
status: "success",
signature: "2ZE7Rz...",
poolId: "7nxQB...",
message: "Successfully created Raydium AMM V4 pool"
},
explanation: "Create a USDC-SOL V4 pool with initial liquidity and price"
}
]
],
schema: z.object({
baseMint: z.string()
.min(1)
.describe("The base token mint address"),
quoteMint: z.string()
.min(1)
.describe("The quote token mint address"),
baseAmount: z.number()
.positive()
.describe("Initial base token amount to provide as liquidity"),
quoteAmount: z.number()
.positive()
.describe("Initial quote token amount to provide as liquidity"),
startPrice: z.number()
.positive()
.describe("Initial price of quote token in base token units"),
openTime: z.number()
.positive()
.describe("Unix timestamp when trading should start")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const marketId = new PublicKey(input.marketId);
const baseAmount = new BN(input.baseAmount);
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 });
return {
status: "success",
signature: txId,
message: "Successfully created Raydium AMM V4 pool"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create AMM V4 pool: ${error.message}`
};
}
}
};
export default raydiumCreateAmmV4Action;

View File

@@ -0,0 +1,142 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import {
CREATE_CPMM_POOL_FEE_ACC,
CREATE_CPMM_POOL_PROGRAM,
Raydium,
TxVersion,
} from "@raydium-io/raydium-sdk-v2";
import { MintLayout } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
const raydiumCreateCpmmAction: Action = {
name: "solana_raydium_create_cpmm",
similes: [
"create raydium pool",
"setup raydium liquidity pool",
"initialize raydium amm",
"create constant product market maker",
"setup raydium cpmm",
"create raydium trading pair"
],
description: "Create a new Constant Product Market Maker (CPMM) pool on Raydium",
examples: [
[
{
input: {
baseMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
quoteMint: "So11111111111111111111111111111111111111112", // SOL
baseAmount: 1000,
quoteAmount: 10,
startTime: 1672531200 // Unix timestamp
},
output: {
status: "success",
signature: "2ZE7Rz...",
poolId: "7nxQB...",
message: "Successfully created Raydium CPMM pool"
},
explanation: "Create a USDC-SOL pool with initial liquidity of 1000 USDC and 10 SOL"
}
]
],
schema: z.object({
baseMint: z.string()
.min(1)
.describe("The base token mint address"),
quoteMint: z.string()
.min(1)
.describe("The quote token mint address"),
baseAmount: z.number()
.positive()
.describe("Initial base token amount to provide as liquidity"),
quoteAmount: z.number()
.positive()
.describe("Initial quote token amount to provide as liquidity"),
startTime: z.number()
.positive()
.describe("Unix timestamp when trading should start")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const mintA = new PublicKey(input.baseMint);
const mintB = new PublicKey(input.quoteMint);
const configId = new PublicKey(input.configId);
const mintAAmount = new BN(input.baseAmount);
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 });
return {
status: "success",
signature: txId,
message: "Successfully created Raydium CPMM pool"
};
} catch (error: any) {
return {
status: "error",
message: `Failed to create CPMM pool: ${error.message}`
};
}
}
};
export default raydiumCreateCpmmAction;

View File

@@ -0,0 +1,110 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { Transaction } from "@solana/web3.js";
import { registerDomainNameV2 } from "@bonfida/spl-name-service";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { TOKENS } from "../constants";
const registerDomainAction: Action = {
name: "solana_register_domain",
similes: [
"register domain",
"buy domain",
"get domain name",
"register .sol",
"purchase domain",
"domain registration"
],
description: "Register a .sol domain name using Bonfida Name Service",
examples: [
[
{
input: {
name: "mydomain",
spaceKB: 1
},
output: {
status: "success",
signature: "2ZE7Rz...",
message: "Successfully registered mydomain.sol"
},
explanation: "Register a new .sol domain with 1KB storage space"
}
]
],
schema: z.object({
name: z.string()
.min(1)
.describe("Domain name to register (without .sol)"),
spaceKB: z.number()
.min(1)
.max(10)
.default(1)
.describe("Space allocation in KB (max 10KB)")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
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,
});
return {
status: "success",
signature,
message: `Successfully registered ${name}.sol`
};
} catch (error: any) {
return {
status: "error",
message: `Domain registration failed: ${error.message}`
};
}
}
};
export default registerDomainAction;

View File

@@ -0,0 +1,61 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { TldParser } from "@onsol/tldparser";
const resolveDomainAction: Action = {
name: "solana_resolve_domain",
similes: [
"resolve domain",
"lookup domain",
"get domain owner",
"check domain",
"find domain owner"
],
description: "Resolve a Solana domain name to get its owner's public key",
examples: [
[
{
input: {
domain: "example.sol"
},
output: {
status: "success",
owner: "7nxQB..."
},
explanation: "Resolve a .sol domain name to get the owner's public key"
}
]
],
schema: z.object({
domain: z.string().min(1).describe("The domain name to resolve")
}),
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"
};
}
return {
status: "success",
owner: tld.toBase58(),
message: `Successfully resolved domain ${domain}`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to resolve domain: ${error.message}`
};
}
}
};
export default resolveDomainAction;

View File

@@ -0,0 +1,69 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { resolve } from "@bonfida/spl-name-service";
const resolveSolDomainAction: Action = {
name: "solana_resolve_sol_domain",
similes: [
"resolve sol domain",
"lookup sol domain",
"get sol domain owner",
"check sol domain",
"find sol domain owner",
"resolve .sol"
],
description: "Resolve a .sol domain to its corresponding Solana wallet address using Bonfida Name Service",
examples: [
[
{
input: {
domain: "vitalik.sol"
},
output: {
status: "success",
owner: "7nxQB...",
message: "Successfully resolved vitalik.sol"
},
explanation: "Resolve a .sol domain to get the owner's wallet address"
}
]
],
schema: z.object({
domain: z.string()
.min(1)
.describe("The .sol domain to resolve (with or without .sol suffix)")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
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);
return {
status: "success",
owner: ownerAddress.toBase58(),
message: `Successfully resolved ${cleanDomain}.sol`
};
} catch (error: any) {
return {
status: "error",
message: `Failed to resolve domain: ${error.message}`
};
}
}
};
export default resolveSolDomainAction;

101
src/actions/stakeWithJup.ts Normal file
View File

@@ -0,0 +1,101 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { VersionedTransaction } from "@solana/web3.js";
const stakeWithJupAction: Action = {
name: "solana_stake_with_jup",
similes: [
"stake sol",
"stake with jupiter",
"jup staking",
"stake with jup",
"liquid staking",
"get jupsol"
],
description: "Stake SOL tokens with Jupiter's liquid staking protocol to receive jupSOL",
examples: [
[
{
input: {
amount: 1.5
},
output: {
status: "success",
signature: "5KtPn3...",
message: "Successfully staked 1.5 SOL for jupSOL"
},
explanation: "Stake 1.5 SOL to receive jupSOL tokens"
}
]
],
schema: z.object({
amount: z.number()
.positive()
.describe("Amount of SOL to stake")
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
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,
});
return {
status: "success",
signature,
message: `Successfully staked ${amount} SOL for jupSOL`
};
} catch (error: any) {
return {
status: "error",
message: `jupSOL staking failed: ${error.message}`
};
}
}
};
export default stakeWithJupAction;