From 8421de5066c19f7550d0fbdb0ba4def37a0dcafb Mon Sep 17 00:00:00 2001 From: Sir Date: Sun, 1 Feb 2026 16:51:35 -0500 Subject: [PATCH 1/6] feat(defi): add Torch Market integration Torch Market is a fair-launch token platform on Solana with bonding curves and community treasuries. After tokens graduate (200 SOL), holders vote to burn or return treasury tokens. Tools: - torchListTokens: List/filter tokens by status - torchGetToken: Get token details - torchBuyToken: Buy on bonding curve - torchSellToken: Sell back to curve - torchVoteToken: Vote on treasury outcome - torchStarToken: Star tokens (0.05 SOL) Actions: - TORCH_LIST_TOKENS - TORCH_GET_TOKEN - TORCH_BUY_TOKEN - TORCH_SELL_TOKEN - TORCH_VOTE_TOKEN - TORCH_STAR_TOKEN API: https://torch.market/api/v1 Docs: https://torch.market/api/v1/openapi.json --- packages/plugin-defi/src/index.ts | 34 ++ .../plugin-defi/src/torch/actions/index.ts | 304 ++++++++++++++++++ packages/plugin-defi/src/torch/tools/index.ts | 205 ++++++++++++ 3 files changed, 543 insertions(+) create mode 100644 packages/plugin-defi/src/torch/actions/index.ts create mode 100644 packages/plugin-defi/src/torch/tools/index.ts diff --git a/packages/plugin-defi/src/index.ts b/packages/plugin-defi/src/index.ts index ef3b8ca53..d53d1e37e 100644 --- a/packages/plugin-defi/src/index.ts +++ b/packages/plugin-defi/src/index.ts @@ -219,6 +219,24 @@ import { executeSwap, } from "./okx/tools"; +// Import Torch tools & actions +import { + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, +} from "./torch/tools"; +import { + torchListTokensAction, + torchGetTokenAction, + torchBuyTokenAction, + torchSellTokenAction, + torchVoteTokenAction, + torchStarTokenAction, +} from "./torch/actions"; + // Define and export the plugin const DefiPlugin = { name: "defi", @@ -338,6 +356,14 @@ const DefiPlugin = { getLiquidity, getChainData, executeSwap, + + // Torch methods + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, }, // Combine all actions @@ -443,6 +469,14 @@ const DefiPlugin = { getLiquidityAction, getChainDataAction, executeSwapAction, + + // Torch actions + torchListTokensAction, + torchGetTokenAction, + torchBuyTokenAction, + torchSellTokenAction, + torchVoteTokenAction, + torchStarTokenAction, ], // Initialize function diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts new file mode 100644 index 000000000..53b9a8c1d --- /dev/null +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -0,0 +1,304 @@ +import { Action, SolanaAgentKit } from "solana-agent-kit"; +import { z } from "zod"; +import { + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, +} from "../tools"; + +export const torchListTokensAction: Action = { + name: "TORCH_LIST_TOKENS", + similes: [ + "list torch tokens", + "browse torch market", + "find bonding curve tokens", + "show torch launchpad tokens", + "what tokens are on torch market", + ], + description: + "List tokens on Torch Market - a fair-launch platform with bonding curves and community treasuries. Filter by status (bonding, complete, migrated) and sort by newest, volume, or marketcap.", + examples: [ + [ + { + input: { status: "bonding", sort: "volume", limit: 10 }, + output: { + status: "success", + tokens: [{ mint: "ABC...", name: "Example", symbol: "EX", progress_percent: 45 }], + count: 10, + }, + explanation: "List the top 10 bonding tokens by volume on Torch Market", + }, + ], + ], + schema: z.object({ + status: z + .enum(["bonding", "complete", "migrated", "all"]) + .optional() + .describe("Filter by token status"), + sort: z + .enum(["newest", "volume", "marketcap"]) + .optional() + .describe("Sort order"), + limit: z + .number() + .positive() + .max(100) + .optional() + .describe("Number of tokens to return"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const tokens = await torchListTokens(agent, input.status, input.sort, input.limit); + return { + status: "success", + tokens, + count: tokens.length, + message: `Found ${tokens.length} tokens on Torch Market`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to list tokens: ${error.message}`, + }; + } + }, +}; + +export const torchGetTokenAction: Action = { + name: "TORCH_GET_TOKEN", + similes: [ + "get torch token info", + "torch token details", + "check torch token", + "lookup token on torch", + ], + description: + "Get detailed information about a specific token on Torch Market, including price, progress, treasury state, and vote counts.", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + token: { name: "Example", symbol: "EX", progress_percent: 45, votes_burn: 100 }, + }, + explanation: "Get details for a specific Torch token", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const token = await torchGetToken(agent, input.mint); + return { + status: "success", + token, + message: `${token.name} (${token.symbol}) - ${token.progress_percent.toFixed(1)}% complete`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get token: ${error.message}`, + }; + } + }, +}; + +export const torchBuyTokenAction: Action = { + name: "TORCH_BUY_TOKEN", + similes: [ + "buy token on torch", + "purchase torch token", + "buy on torch market", + "invest in torch token", + ], + description: + "Buy tokens on Torch Market bonding curve. Specify amount in SOL. 10% of tokens go to community treasury, 90% to you. 1% protocol fee on buys.", + examples: [ + [ + { + input: { mint: "ABC123...", amountSol: 0.1 }, + output: { + status: "success", + signature: "5xKp...", + message: "Bought tokens for 0.1 SOL", + }, + explanation: "Buy tokens with 0.1 SOL on Torch Market", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + amountSol: z.number().positive().describe("Amount of SOL to spend"), + slippagePercent: z + .number() + .positive() + .max(50) + .optional() + .describe("Slippage tolerance as percentage (default 1%)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const lamports = Math.floor(input.amountSol * 1e9); + const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; + const signature = await torchBuyToken(agent, input.mint, lamports, bps); + return { + status: "success", + signature, + message: `Bought tokens for ${input.amountSol} SOL`, + }; + } catch (error: any) { + return { + status: "error", + message: `Buy failed: ${error.message}`, + }; + } + }, +}; + +export const torchSellTokenAction: Action = { + name: "TORCH_SELL_TOKEN", + similes: [ + "sell token on torch", + "sell torch token", + "exit torch position", + ], + description: + "Sell tokens back to Torch Market bonding curve. No sell fees. Specify amount in tokens.", + examples: [ + [ + { + input: { mint: "ABC123...", amountTokens: 1000000 }, + output: { + status: "success", + signature: "5xKp...", + message: "Sold 1M tokens", + }, + explanation: "Sell 1M tokens back to the bonding curve", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + amountTokens: z.number().positive().describe("Amount of tokens to sell"), + slippagePercent: z + .number() + .positive() + .max(50) + .optional() + .describe("Slippage tolerance as percentage (default 1%)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const baseUnits = Math.floor(input.amountTokens * 1e6); + const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; + const signature = await torchSellToken(agent, input.mint, baseUnits, bps); + return { + status: "success", + signature, + message: `Sold ${input.amountTokens.toLocaleString()} tokens`, + }; + } catch (error: any) { + return { + status: "error", + message: `Sell failed: ${error.message}`, + }; + } + }, +}; + +export const torchVoteTokenAction: Action = { + name: "TORCH_VOTE_TOKEN", + similes: [ + "vote on torch token", + "torch treasury vote", + "vote burn torch", + "vote return torch", + ], + description: + "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply), 'return' gives them to the creator. You must hold the token to vote.", + examples: [ + [ + { + input: { mint: "ABC123...", vote: "burn" }, + output: { + status: "success", + signature: "5xKp...", + message: "Voted to burn treasury tokens", + }, + explanation: "Vote to burn the community treasury tokens", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + vote: z + .enum(["burn", "return"]) + .describe("'burn' = destroy treasury tokens, 'return' = give to creator"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchVoteToken(agent, input.mint, input.vote); + const desc = input.vote === "burn" ? "burn treasury tokens" : "return tokens to creator"; + return { + status: "success", + signature, + vote: input.vote, + message: `Voted to ${desc}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Vote failed: ${error.message}`, + }; + } + }, +}; + +export const torchStarTokenAction: Action = { + name: "TORCH_STAR_TOKEN", + similes: [ + "star torch token", + "support torch token", + "like token on torch", + ], + description: + "Star a token on Torch Market to show support (costs 0.05 SOL). When tokens reach the star threshold, creators receive rewards.", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + signature: "5xKp...", + message: "Starred token for 0.05 SOL", + }, + explanation: "Star a token to show support", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address to star"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchStarToken(agent, input.mint); + return { + status: "success", + signature, + cost: 0.05, + message: "Starred token (cost: 0.05 SOL)", + }; + } catch (error: any) { + return { + status: "error", + message: `Star failed: ${error.message}`, + }; + } + }, +}; diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts new file mode 100644 index 000000000..55838d438 --- /dev/null +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -0,0 +1,205 @@ +import { Transaction } from "@solana/web3.js"; +import { type SolanaAgentKit, signOrSendTX } from "solana-agent-kit"; + +const TORCH_API = "https://torch.market/api/v1"; + +export interface TorchToken { + mint: string; + name: string; + symbol: string; + status: "bonding" | "complete" | "migrated"; + price_sol: number; + market_cap_sol: number; + progress_percent: number; + holders: number; + created_at: number; +} + +export interface TorchTokenDetail extends TorchToken { + description?: string; + image?: string; + sol_raised: number; + sol_target: number; + total_supply: number; + circulating_supply: number; + treasury_sol_balance: number; + treasury_token_balance: number; + votes_return: number; + votes_burn: number; + creator: string; + stars: number; +} + +/** + * List tokens on Torch Market + * @param agent SolanaAgentKit instance + * @param status Filter by status: bonding, complete, migrated, or all + * @param sort Sort by: newest, volume, or marketcap + * @param limit Number of tokens to return (max 100) + * @returns Array of token summaries + */ +export async function torchListTokens( + agent: SolanaAgentKit, + status?: "bonding" | "complete" | "migrated" | "all", + sort?: "newest" | "volume" | "marketcap", + limit?: number, +): Promise { + const params = new URLSearchParams(); + if (status) params.set("status", status); + if (sort) params.set("sort", sort); + if (limit) params.set("limit", limit.toString()); + + const res = await fetch(`${TORCH_API}/tokens?${params}`); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to list tokens"); + return json.data.tokens; +} + +/** + * Get detailed information about a token + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @returns Token details including treasury state and votes + */ +export async function torchGetToken( + agent: SolanaAgentKit, + mint: string, +): Promise { + const res = await fetch(`${TORCH_API}/tokens/${mint}`); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Token not found"); + return json.data; +} + +/** + * Buy tokens on Torch Market bonding curve + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param amountLamports Amount of SOL in lamports (1 SOL = 1e9 lamports) + * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @returns Transaction signature + */ +export async function torchBuyToken( + agent: SolanaAgentKit, + mint: string, + amountLamports: number, + slippageBps: number = 100, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/buy`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + buyer: agent.wallet.publicKey.toBase58(), + amount_sol: amountLamports, + slippage_bps: slippageBps, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build buy transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Sell tokens back to Torch Market bonding curve + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param amountTokens Amount of tokens in base units (6 decimals) + * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @returns Transaction signature + */ +export async function torchSellToken( + agent: SolanaAgentKit, + mint: string, + amountTokens: number, + slippageBps: number = 100, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/sell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + seller: agent.wallet.publicKey.toBase58(), + amount_tokens: amountTokens, + slippage_bps: slippageBps, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build sell transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Vote on treasury outcome for a graduated token + * + * After a token reaches 200 SOL, it graduates and holders vote on the + * community treasury (10% of all tokens bought): + * - "burn": Destroy the tokens, reducing total supply + * - "return": Return the tokens to the token creator + * + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param vote Vote choice: "burn" or "return" + * @returns Transaction signature + */ +export async function torchVoteToken( + agent: SolanaAgentKit, + mint: string, + vote: "burn" | "return", +): Promise { + const res = await fetch(`${TORCH_API}/transactions/vote`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + voter: agent.wallet.publicKey.toBase58(), + vote, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build vote transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Star a token to show support (costs 0.05 SOL) + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @returns Transaction signature + */ +export async function torchStarToken( + agent: SolanaAgentKit, + mint: string, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/star`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + user: agent.wallet.publicKey.toBase58(), + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build star transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} From 2e9b7cdaf52539a9184bda2a265d92e2b8bed582 Mon Sep 17 00:00:00 2001 From: Sir Date: Sun, 1 Feb 2026 16:51:35 -0500 Subject: [PATCH 2/6] feat(defi): add Torch Market integration Torch Market is a fair-launch token platform on Solana with bonding curves and community treasuries. After tokens graduate (200 SOL), holders vote to burn or return treasury tokens. Tools: - torchListTokens: List/filter tokens by status - torchGetToken: Get token details - torchBuyToken: Buy on bonding curve - torchSellToken: Sell back to curve - torchVoteToken: Vote on treasury outcome - torchStarToken: Star tokens (0.05 SOL) - torchCreateToken: Create a new token with bonding curve Actions: - TORCH_LIST_TOKENS - TORCH_GET_TOKEN - TORCH_BUY_TOKEN - TORCH_SELL_TOKEN - TORCH_VOTE_TOKEN - TORCH_STAR_TOKEN - TORCH_CREATE_TOKEN API: https://torch.market/api/v1 Docs: https://torch.market/api/v1/openapi.json --- packages/plugin-defi/src/index.ts | 38 ++ .../plugin-defi/src/torch/actions/index.ts | 362 ++++++++++++++++++ packages/plugin-defi/src/torch/tools/index.ts | 255 ++++++++++++ 3 files changed, 655 insertions(+) create mode 100644 packages/plugin-defi/src/torch/actions/index.ts create mode 100644 packages/plugin-defi/src/torch/tools/index.ts diff --git a/packages/plugin-defi/src/index.ts b/packages/plugin-defi/src/index.ts index ef3b8ca53..37237d897 100644 --- a/packages/plugin-defi/src/index.ts +++ b/packages/plugin-defi/src/index.ts @@ -219,6 +219,26 @@ import { executeSwap, } from "./okx/tools"; +// Import Torch tools & actions +import { + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, + torchCreateToken, +} from "./torch/tools"; +import { + torchListTokensAction, + torchGetTokenAction, + torchBuyTokenAction, + torchSellTokenAction, + torchVoteTokenAction, + torchStarTokenAction, + torchCreateTokenAction, +} from "./torch/actions"; + // Define and export the plugin const DefiPlugin = { name: "defi", @@ -338,6 +358,15 @@ const DefiPlugin = { getLiquidity, getChainData, executeSwap, + + // Torch methods + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, + torchCreateToken, }, // Combine all actions @@ -443,6 +472,15 @@ const DefiPlugin = { getLiquidityAction, getChainDataAction, executeSwapAction, + + // Torch actions + torchListTokensAction, + torchGetTokenAction, + torchBuyTokenAction, + torchSellTokenAction, + torchVoteTokenAction, + torchStarTokenAction, + torchCreateTokenAction, ], // Initialize function diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts new file mode 100644 index 000000000..717a66f50 --- /dev/null +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -0,0 +1,362 @@ +import { Action, SolanaAgentKit } from "solana-agent-kit"; +import { z } from "zod"; +import { + torchListTokens, + torchGetToken, + torchBuyToken, + torchSellToken, + torchVoteToken, + torchStarToken, + torchCreateToken, +} from "../tools"; + +export const torchListTokensAction: Action = { + name: "TORCH_LIST_TOKENS", + similes: [ + "list torch tokens", + "browse torch market", + "find bonding curve tokens", + "show torch launchpad tokens", + "what tokens are on torch market", + ], + description: + "List tokens on Torch Market - a fair-launch platform with bonding curves and community treasuries. Filter by status (bonding, complete, migrated) and sort by newest, volume, or marketcap.", + examples: [ + [ + { + input: { status: "bonding", sort: "volume", limit: 10 }, + output: { + status: "success", + tokens: [{ mint: "ABC...", name: "Example", symbol: "EX", progress_percent: 45 }], + count: 10, + }, + explanation: "List the top 10 bonding tokens by volume on Torch Market", + }, + ], + ], + schema: z.object({ + status: z + .enum(["bonding", "complete", "migrated", "all"]) + .optional() + .describe("Filter by token status"), + sort: z + .enum(["newest", "volume", "marketcap"]) + .optional() + .describe("Sort order"), + limit: z + .number() + .positive() + .max(100) + .optional() + .describe("Number of tokens to return"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const tokens = await torchListTokens(agent, input.status, input.sort, input.limit); + return { + status: "success", + tokens, + count: tokens.length, + message: `Found ${tokens.length} tokens on Torch Market`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to list tokens: ${error.message}`, + }; + } + }, +}; + +export const torchGetTokenAction: Action = { + name: "TORCH_GET_TOKEN", + similes: [ + "get torch token info", + "torch token details", + "check torch token", + "lookup token on torch", + ], + description: + "Get detailed information about a specific token on Torch Market, including price, progress, treasury state, and vote counts.", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + token: { name: "Example", symbol: "EX", progress_percent: 45, votes_burn: 100 }, + }, + explanation: "Get details for a specific Torch token", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const token = await torchGetToken(agent, input.mint); + return { + status: "success", + token, + message: `${token.name} (${token.symbol}) - ${token.progress_percent.toFixed(1)}% complete`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get token: ${error.message}`, + }; + } + }, +}; + +export const torchBuyTokenAction: Action = { + name: "TORCH_BUY_TOKEN", + similes: [ + "buy token on torch", + "purchase torch token", + "buy on torch market", + "invest in torch token", + ], + description: + "Buy tokens on Torch Market bonding curve. Specify amount in SOL. 10% of tokens go to community treasury, 90% to you. 1% protocol fee on buys.", + examples: [ + [ + { + input: { mint: "ABC123...", amountSol: 0.1 }, + output: { + status: "success", + signature: "5xKp...", + message: "Bought tokens for 0.1 SOL", + }, + explanation: "Buy tokens with 0.1 SOL on Torch Market", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + amountSol: z.number().positive().describe("Amount of SOL to spend"), + slippagePercent: z + .number() + .positive() + .max(50) + .optional() + .describe("Slippage tolerance as percentage (default 1%)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const lamports = Math.floor(input.amountSol * 1e9); + const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; + const signature = await torchBuyToken(agent, input.mint, lamports, bps); + return { + status: "success", + signature, + message: `Bought tokens for ${input.amountSol} SOL`, + }; + } catch (error: any) { + return { + status: "error", + message: `Buy failed: ${error.message}`, + }; + } + }, +}; + +export const torchSellTokenAction: Action = { + name: "TORCH_SELL_TOKEN", + similes: [ + "sell token on torch", + "sell torch token", + "exit torch position", + ], + description: + "Sell tokens back to Torch Market bonding curve. No sell fees. Specify amount in tokens.", + examples: [ + [ + { + input: { mint: "ABC123...", amountTokens: 1000000 }, + output: { + status: "success", + signature: "5xKp...", + message: "Sold 1M tokens", + }, + explanation: "Sell 1M tokens back to the bonding curve", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + amountTokens: z.number().positive().describe("Amount of tokens to sell"), + slippagePercent: z + .number() + .positive() + .max(50) + .optional() + .describe("Slippage tolerance as percentage (default 1%)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const baseUnits = Math.floor(input.amountTokens * 1e6); + const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; + const signature = await torchSellToken(agent, input.mint, baseUnits, bps); + return { + status: "success", + signature, + message: `Sold ${input.amountTokens.toLocaleString()} tokens`, + }; + } catch (error: any) { + return { + status: "error", + message: `Sell failed: ${error.message}`, + }; + } + }, +}; + +export const torchVoteTokenAction: Action = { + name: "TORCH_VOTE_TOKEN", + similes: [ + "vote on torch token", + "torch treasury vote", + "vote burn torch", + "vote return torch", + ], + description: + "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply), 'return' gives them to the creator. You must hold the token to vote.", + examples: [ + [ + { + input: { mint: "ABC123...", vote: "burn" }, + output: { + status: "success", + signature: "5xKp...", + message: "Voted to burn treasury tokens", + }, + explanation: "Vote to burn the community treasury tokens", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + vote: z + .enum(["burn", "return"]) + .describe("'burn' = destroy treasury tokens, 'return' = give to creator"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchVoteToken(agent, input.mint, input.vote); + const desc = input.vote === "burn" ? "burn treasury tokens" : "return tokens to creator"; + return { + status: "success", + signature, + vote: input.vote, + message: `Voted to ${desc}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Vote failed: ${error.message}`, + }; + } + }, +}; + +export const torchStarTokenAction: Action = { + name: "TORCH_STAR_TOKEN", + similes: [ + "star torch token", + "support torch token", + "like token on torch", + ], + description: + "Star a token on Torch Market to show support (costs 0.05 SOL). When tokens reach the star threshold, creators receive rewards.", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + signature: "5xKp...", + message: "Starred token for 0.05 SOL", + }, + explanation: "Star a token to show support", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address to star"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchStarToken(agent, input.mint); + return { + status: "success", + signature, + cost: 0.05, + message: "Starred token (cost: 0.05 SOL)", + }; + } catch (error: any) { + return { + status: "error", + message: `Star failed: ${error.message}`, + }; + } + }, +}; + +export const torchCreateTokenAction: Action = { + name: "TORCH_CREATE_TOKEN", + similes: [ + "create token on torch", + "launch token on torch", + "create my own token", + "make a torch token", + "deploy token to torch market", + ], + description: + "Create a new token on Torch Market with automatic bonding curve, community treasury, and Raydium migration. You need to provide a metadata_uri pointing to a JSON file with name, symbol, description, and image URL.", + examples: [ + [ + { + input: { + name: "My Agent Token", + symbol: "MAT", + metadataUri: "https://arweave.net/abc123", + }, + output: { + status: "success", + signature: "5xKp...", + mint: "NEW_MINT_ADDRESS", + message: "Created token My Agent Token ($MAT)", + }, + explanation: "Create a new token with bonding curve", + }, + ], + ], + schema: z.object({ + name: z.string().max(32).describe("Token name (max 32 characters)"), + symbol: z.string().max(10).describe("Token symbol (max 10 characters)"), + metadataUri: z.string().url().describe("URI pointing to token metadata JSON with name, symbol, description, and image"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const result = await torchCreateToken( + agent, + input.name, + input.symbol, + input.metadataUri, + ); + return { + status: "success", + signature: result.signature, + mint: result.mint, + message: `Created token ${input.name} ($${input.symbol})`, + }; + } catch (error: any) { + return { + status: "error", + message: `Create token failed: ${error.message}`, + }; + } + }, +}; diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts new file mode 100644 index 000000000..e647faed4 --- /dev/null +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -0,0 +1,255 @@ +import { Transaction } from "@solana/web3.js"; +import { type SolanaAgentKit, signOrSendTX } from "solana-agent-kit"; + +const TORCH_API = "https://torch.market/api/v1"; + +export interface TorchToken { + mint: string; + name: string; + symbol: string; + status: "bonding" | "complete" | "migrated"; + price_sol: number; + market_cap_sol: number; + progress_percent: number; + holders: number; + created_at: number; +} + +export interface TorchTokenDetail extends TorchToken { + description?: string; + image?: string; + sol_raised: number; + sol_target: number; + total_supply: number; + circulating_supply: number; + treasury_sol_balance: number; + treasury_token_balance: number; + votes_return: number; + votes_burn: number; + creator: string; + stars: number; +} + +/** + * List tokens on Torch Market + * @param agent SolanaAgentKit instance + * @param status Filter by status: bonding, complete, migrated, or all + * @param sort Sort by: newest, volume, or marketcap + * @param limit Number of tokens to return (max 100) + * @returns Array of token summaries + */ +export async function torchListTokens( + agent: SolanaAgentKit, + status?: "bonding" | "complete" | "migrated" | "all", + sort?: "newest" | "volume" | "marketcap", + limit?: number, +): Promise { + const params = new URLSearchParams(); + if (status) params.set("status", status); + if (sort) params.set("sort", sort); + if (limit) params.set("limit", limit.toString()); + + const res = await fetch(`${TORCH_API}/tokens?${params}`); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to list tokens"); + return json.data.tokens; +} + +/** + * Get detailed information about a token + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @returns Token details including treasury state and votes + */ +export async function torchGetToken( + agent: SolanaAgentKit, + mint: string, +): Promise { + const res = await fetch(`${TORCH_API}/tokens/${mint}`); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Token not found"); + return json.data; +} + +/** + * Buy tokens on Torch Market bonding curve + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param amountLamports Amount of SOL in lamports (1 SOL = 1e9 lamports) + * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @returns Transaction signature + */ +export async function torchBuyToken( + agent: SolanaAgentKit, + mint: string, + amountLamports: number, + slippageBps: number = 100, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/buy`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + buyer: agent.wallet.publicKey.toBase58(), + amount_sol: amountLamports, + slippage_bps: slippageBps, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build buy transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Sell tokens back to Torch Market bonding curve + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param amountTokens Amount of tokens in base units (6 decimals) + * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @returns Transaction signature + */ +export async function torchSellToken( + agent: SolanaAgentKit, + mint: string, + amountTokens: number, + slippageBps: number = 100, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/sell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + seller: agent.wallet.publicKey.toBase58(), + amount_tokens: amountTokens, + slippage_bps: slippageBps, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build sell transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Vote on treasury outcome for a graduated token + * + * After a token reaches 200 SOL, it graduates and holders vote on the + * community treasury (10% of all tokens bought): + * - "burn": Destroy the tokens, reducing total supply + * - "return": Return the tokens to the token creator + * + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param vote Vote choice: "burn" or "return" + * @returns Transaction signature + */ +export async function torchVoteToken( + agent: SolanaAgentKit, + mint: string, + vote: "burn" | "return", +): Promise { + const res = await fetch(`${TORCH_API}/transactions/vote`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + voter: agent.wallet.publicKey.toBase58(), + vote, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build vote transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Star a token to show support (costs 0.05 SOL) + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @returns Transaction signature + */ +export async function torchStarToken( + agent: SolanaAgentKit, + mint: string, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/star`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + user: agent.wallet.publicKey.toBase58(), + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build star transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +export interface CreateTokenResult { + signature: string; + mint: string; +} + +/** + * Create a new token on Torch Market with automatic bonding curve + * + * This allows AI agents to launch their own tokens. The token will have: + * - Automatic bonding curve for price discovery + * - Community treasury (10% of buys) + * - Graduation at 200 SOL with Raydium migration + * - Democratic voting on treasury outcome + * + * @param agent SolanaAgentKit instance + * @param name Token name (max 32 characters) + * @param symbol Token symbol (max 10 characters) + * @param metadataUri URI pointing to token metadata JSON (Metaplex standard) + * @returns Transaction signature and new token mint address + */ +export async function torchCreateToken( + agent: SolanaAgentKit, + name: string, + symbol: string, + metadataUri: string, +): Promise { + const res = await fetch(`${TORCH_API}/transactions/create`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + creator: agent.wallet.publicKey.toBase58(), + name, + symbol, + metadata_uri: metadataUri, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build create transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + const signature = await signOrSendTX(agent, tx); + return { + signature, + mint: json.data.mint, + }; +} From 402e931c7306ad541464c69882863934c961db6b Mon Sep 17 00:00:00 2001 From: Sir Date: Sun, 1 Feb 2026 18:02:14 -0500 Subject: [PATCH 3/6] update build with messages --- packages/plugin-defi/src/index.ts | 8 ++ .../plugin-defi/src/torch/actions/index.ts | 98 +++++++++++++++++++ packages/plugin-defi/src/torch/tools/index.ts | 71 ++++++++++++-- 3 files changed, 170 insertions(+), 7 deletions(-) diff --git a/packages/plugin-defi/src/index.ts b/packages/plugin-defi/src/index.ts index 37237d897..8b83fa353 100644 --- a/packages/plugin-defi/src/index.ts +++ b/packages/plugin-defi/src/index.ts @@ -228,6 +228,8 @@ import { torchVoteToken, torchStarToken, torchCreateToken, + torchGetMessages, + torchPostMessage, } from "./torch/tools"; import { torchListTokensAction, @@ -237,6 +239,8 @@ import { torchVoteTokenAction, torchStarTokenAction, torchCreateTokenAction, + torchGetMessagesAction, + torchPostMessageAction, } from "./torch/actions"; // Define and export the plugin @@ -367,6 +371,8 @@ const DefiPlugin = { torchVoteToken, torchStarToken, torchCreateToken, + torchGetMessages, + torchPostMessage, }, // Combine all actions @@ -481,6 +487,8 @@ const DefiPlugin = { torchVoteTokenAction, torchStarTokenAction, torchCreateTokenAction, + torchGetMessagesAction, + torchPostMessageAction, ], // Initialize function diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts index 717a66f50..f4b07144f 100644 --- a/packages/plugin-defi/src/torch/actions/index.ts +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -8,6 +8,8 @@ import { torchVoteToken, torchStarToken, torchCreateToken, + torchGetMessages, + torchPostMessage, } from "../tools"; export const torchListTokensAction: Action = { @@ -360,3 +362,99 @@ export const torchCreateTokenAction: Action = { } }, }; + +export const torchGetMessagesAction: Action = { + name: "TORCH_GET_MESSAGES", + similes: [ + "get torch messages", + "read torch messages", + "see messages on torch", + "what are agents saying on torch", + "read token chat", + ], + description: + "Get messages from a token's page on Torch Market. AI agents can use this to read what other agents are saying and coordinate.", + examples: [ + [ + { + input: { mint: "ABC123...", limit: 20 }, + output: { + status: "success", + messages: [{ memo: "Hello from an AI!", sender: "5xKp...", timestamp: 1234567890 }], + count: 1, + }, + explanation: "Read the last 20 messages on a token's page", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + limit: z + .number() + .positive() + .max(100) + .optional() + .describe("Number of messages to return (default 50, max 100)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const messages = await torchGetMessages(agent, input.mint, input.limit); + return { + status: "success", + messages, + count: messages.length, + message: `Found ${messages.length} messages`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get messages: ${error.message}`, + }; + } + }, +}; + +export const torchPostMessageAction: Action = { + name: "TORCH_POST_MESSAGE", + similes: [ + "post torch message", + "send torch message", + "say something on torch", + "communicate on torch", + "message other agents", + ], + description: + "Post a message on a token's page on Torch Market. AI agents can use this to communicate with each other. Messages are stored on-chain as SPL Memos and are permanent.", + examples: [ + [ + { + input: { mint: "ABC123...", message: "Hello from an AI agent!" }, + output: { + status: "success", + signature: "5xKp...", + message: "Posted message", + }, + explanation: "Post a message on a token's page", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + message: z.string().max(500).describe("Message to post (max 500 characters)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchPostMessage(agent, input.mint, input.message); + return { + status: "success", + signature, + message: "Posted message", + }; + } catch (error: any) { + return { + status: "error", + message: `Post message failed: ${error.message}`, + }; + } + }, +}; diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts index e647faed4..5ba494966 100644 --- a/packages/plugin-defi/src/torch/tools/index.ts +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -84,7 +84,7 @@ export async function torchBuyToken( mint: string, amountLamports: number, slippageBps: number = 100, -): Promise { +) { const res = await fetch(`${TORCH_API}/transactions/buy`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -118,7 +118,7 @@ export async function torchSellToken( mint: string, amountTokens: number, slippageBps: number = 100, -): Promise { +) { const res = await fetch(`${TORCH_API}/transactions/sell`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -156,7 +156,7 @@ export async function torchVoteToken( agent: SolanaAgentKit, mint: string, vote: "burn" | "return", -): Promise { +) { const res = await fetch(`${TORCH_API}/transactions/vote`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -185,7 +185,7 @@ export async function torchVoteToken( export async function torchStarToken( agent: SolanaAgentKit, mint: string, -): Promise { +) { const res = await fetch(`${TORCH_API}/transactions/star`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -204,9 +204,11 @@ export async function torchStarToken( return signOrSendTX(agent, tx); } -export interface CreateTokenResult { +export interface TorchMessage { signature: string; - mint: string; + memo: string; + sender: string; + timestamp: number; } /** @@ -229,7 +231,7 @@ export async function torchCreateToken( name: string, symbol: string, metadataUri: string, -): Promise { +) { const res = await fetch(`${TORCH_API}/transactions/create`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -253,3 +255,58 @@ export async function torchCreateToken( mint: json.data.mint, }; } + +/** + * Get messages (memos) from a token's page + * AI agents can use this to read what other agents are saying + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param limit Number of messages to return (max 100) + * @returns Array of messages + */ +export async function torchGetMessages( + agent: SolanaAgentKit, + mint: string, + limit: number = 50, +): Promise { + const params = new URLSearchParams(); + if (limit) params.set("limit", limit.toString()); + + const res = await fetch(`${TORCH_API}/tokens/${mint}/messages?${params}`); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to get messages"); + return json.data.messages; +} + +/** + * Post a message on a token's page + * AI agents can use this to communicate with each other + * Messages are stored on-chain as SPL Memos + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param message Message to post (max 500 characters) + * @returns Transaction signature + */ +export async function torchPostMessage( + agent: SolanaAgentKit, + mint: string, + message: string, +) { + const res = await fetch(`${TORCH_API}/transactions/message`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + sender: agent.wallet.publicKey.toBase58(), + message, + }), + }); + const json = await res.json(); + if (!json.success) throw new Error(json.error?.message || "Failed to build message transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} From 9ba2c00dc6f1e2c8f81b55f3a0829095656bcaba Mon Sep 17 00:00:00 2001 From: Sir Date: Sat, 7 Feb 2026 12:50:47 -0500 Subject: [PATCH 4/6] update agent kit to v2.4 --- packages/plugin-defi/src/index.ts | 24 ++ .../plugin-defi/src/torch/actions/index.ts | 355 +++++++++++++++++- packages/plugin-defi/src/torch/tools/index.ts | 269 +++++++++++-- 3 files changed, 612 insertions(+), 36 deletions(-) diff --git a/packages/plugin-defi/src/index.ts b/packages/plugin-defi/src/index.ts index 8b83fa353..c29e073ae 100644 --- a/packages/plugin-defi/src/index.ts +++ b/packages/plugin-defi/src/index.ts @@ -230,6 +230,12 @@ import { torchCreateToken, torchGetMessages, torchPostMessage, + torchConfirm, + torchGetLendingInfo, + torchGetLoanPosition, + torchBorrowToken, + torchRepayLoan, + torchLiquidateLoan, } from "./torch/tools"; import { torchListTokensAction, @@ -241,6 +247,12 @@ import { torchCreateTokenAction, torchGetMessagesAction, torchPostMessageAction, + torchConfirmAction, + torchGetLendingInfoAction, + torchGetLoanAction, + torchBorrowAction, + torchRepayAction, + torchLiquidateAction, } from "./torch/actions"; // Define and export the plugin @@ -373,6 +385,12 @@ const DefiPlugin = { torchCreateToken, torchGetMessages, torchPostMessage, + torchConfirm, + torchGetLendingInfo, + torchGetLoanPosition, + torchBorrowToken, + torchRepayLoan, + torchLiquidateLoan, }, // Combine all actions @@ -489,6 +507,12 @@ const DefiPlugin = { torchCreateTokenAction, torchGetMessagesAction, torchPostMessageAction, + torchConfirmAction, + torchGetLendingInfoAction, + torchGetLoanAction, + torchBorrowAction, + torchRepayAction, + torchLiquidateAction, ], // Initialize function diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts index f4b07144f..1a4d1dd83 100644 --- a/packages/plugin-defi/src/torch/actions/index.ts +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -10,6 +10,12 @@ import { torchCreateToken, torchGetMessages, torchPostMessage, + torchConfirm, + torchGetLendingInfo, + torchGetLoanPosition, + torchBorrowToken, + torchRepayLoan, + torchLiquidateLoan, } from "../tools"; export const torchListTokensAction: Action = { @@ -22,7 +28,7 @@ export const torchListTokensAction: Action = { "what tokens are on torch market", ], description: - "List tokens on Torch Market - a fair-launch platform with bonding curves and community treasuries. Filter by status (bonding, complete, migrated) and sort by newest, volume, or marketcap.", + "List tokens on Torch Market - a fair-launch DAO launchpad on Solana with bonding curves, community treasuries, and democratic governance. Filter by status (bonding, complete, migrated) and sort by newest, volume, or marketcap.", examples: [ [ { @@ -79,7 +85,7 @@ export const torchGetTokenAction: Action = { "lookup token on torch", ], description: - "Get detailed information about a specific token on Torch Market, including price, progress, treasury state, and vote counts.", + "Get detailed information about a specific token on Torch Market, including price, progress, treasury state, vote counts, and creator SAID verification status.", examples: [ [ { @@ -121,17 +127,28 @@ export const torchBuyTokenAction: Action = { "invest in torch token", ], description: - "Buy tokens on Torch Market bonding curve. Specify amount in SOL. 10% of tokens go to community treasury, 90% to you. 1% protocol fee on buys.", + "Buy tokens on Torch Market bonding curve. Specify amount in SOL. 10% of tokens go to community treasury, 90% to you. 1% protocol fee on buys. On your FIRST buy of a token you must include a vote ('burn' or 'return') for how the treasury should be handled at graduation. You can optionally include a message (max 500 chars) which will be bundled as an on-chain SPL Memo -- skin-in-the-game communication.", examples: [ [ { - input: { mint: "ABC123...", amountSol: 0.1 }, + input: { mint: "ABC123...", amountSol: 0.1, vote: "burn" }, output: { status: "success", signature: "5xKp...", - message: "Bought tokens for 0.1 SOL", + message: "Bought tokens for 0.1 SOL (voted: burn)", }, - explanation: "Buy tokens with 0.1 SOL on Torch Market", + explanation: "First buy requires a vote -- vote burn to reduce supply at graduation", + }, + ], + [ + { + input: { mint: "ABC123...", amountSol: 0.05, message: "Bullish on this project!" }, + output: { + status: "success", + signature: "5xKp...", + message: "Bought tokens for 0.05 SOL with message", + }, + explanation: "Subsequent buy with an on-chain message bundled in", }, ], ], @@ -144,16 +161,28 @@ export const torchBuyTokenAction: Action = { .max(50) .optional() .describe("Slippage tolerance as percentage (default 1%)"), + vote: z + .enum(["burn", "return"]) + .optional() + .describe("Treasury vote -- REQUIRED on first buy. 'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP"), + message: z + .string() + .max(500) + .optional() + .describe("Optional message to bundle as on-chain SPL Memo (max 500 chars). Skin-in-the-game: every message has a provable trade behind it."), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { const lamports = Math.floor(input.amountSol * 1e9); const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; - const signature = await torchBuyToken(agent, input.mint, lamports, bps); + const signature = await torchBuyToken(agent, input.mint, lamports, bps, input.vote, input.message); + const parts = [`Bought tokens for ${input.amountSol} SOL`]; + if (input.vote) parts.push(`(voted: ${input.vote})`); + if (input.message) parts.push("with message"); return { status: "success", signature, - message: `Bought tokens for ${input.amountSol} SOL`, + message: parts.join(" "), }; } catch (error: any) { return { @@ -224,7 +253,7 @@ export const torchVoteTokenAction: Action = { "vote return torch", ], description: - "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply), 'return' gives them to the creator. You must hold the token to vote.", + "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply from 1B to 900M), 'return' adds them to the Raydium LP for deeper liquidity. One wallet, one vote. The result is binding and executed at migration.", examples: [ [ { @@ -242,12 +271,12 @@ export const torchVoteTokenAction: Action = { mint: z.string().describe("Token mint address"), vote: z .enum(["burn", "return"]) - .describe("'burn' = destroy treasury tokens, 'return' = give to creator"), + .describe("'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP (deeper liquidity)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { const signature = await torchVoteToken(agent, input.mint, input.vote); - const desc = input.vote === "burn" ? "burn treasury tokens" : "return tokens to creator"; + const desc = input.vote === "burn" ? "burn treasury tokens (reduce supply)" : "return tokens to LP (deepen liquidity)"; return { status: "success", signature, @@ -271,7 +300,7 @@ export const torchStarTokenAction: Action = { "like token on torch", ], description: - "Star a token on Torch Market to show support (costs 0.05 SOL). When tokens reach the star threshold, creators receive rewards.", + "Star a token on Torch Market to signal sybil-resistant support (costs 0.05 SOL). When tokens reach 2000 stars, creators receive the accumulated ~100 SOL.", examples: [ [ { @@ -316,7 +345,7 @@ export const torchCreateTokenAction: Action = { "deploy token to torch market", ], description: - "Create a new token on Torch Market with automatic bonding curve, community treasury, and Raydium migration. You need to provide a metadata_uri pointing to a JSON file with name, symbol, description, and image URL.", + "Launch a new community on Torch Market with automatic bonding curve, community treasury, governance vote, and Raydium migration. Every token is a DAO seed. You need to provide a metadata_uri pointing to a JSON file with name, symbol, description, and image URL.", examples: [ [ { @@ -373,7 +402,7 @@ export const torchGetMessagesAction: Action = { "read token chat", ], description: - "Get messages from a token's page on Torch Market. AI agents can use this to read what other agents are saying and coordinate.", + "Get messages from a token's page on Torch Market. Messages are bundled with trades, so every message has a provable buy or sell behind it. Use this to read what agents and humans are saying and verify their positions.", examples: [ [ { @@ -424,7 +453,7 @@ export const torchPostMessageAction: Action = { "message other agents", ], description: - "Post a message on a token's page on Torch Market. AI agents can use this to communicate with each other. Messages are stored on-chain as SPL Memos and are permanent.", + "Post a standalone message on a token's page on Torch Market. Prefer using the 'message' parameter on TORCH_BUY_TOKEN instead -- bundling a message with a trade is the preferred approach (skin-in-the-game). This standalone endpoint is for cases where you need to post without a trade.", examples: [ [ { @@ -458,3 +487,299 @@ export const torchPostMessageAction: Action = { } }, }; + +export const torchConfirmAction: Action = { + name: "TORCH_CONFIRM", + similes: [ + "confirm torch transaction", + "report torch transaction", + "torch reputation", + "said reputation torch", + ], + description: + "Report a successful Torch Market transaction to SAID Protocol for reputation. Call this after any confirmed transaction to build your trust score: token launch (+15), trade (+5), governance vote (+10). Requires SAID registration.", + examples: [ + [ + { + input: { signature: "5xKp..." }, + output: { + status: "success", + event_type: "trade_complete", + feedback_sent: true, + message: "Transaction confirmed, +5 reputation", + }, + explanation: "Confirm a trade for SAID reputation points", + }, + ], + ], + schema: z.object({ + signature: z.string().describe("Transaction signature to confirm"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const result = await torchConfirm(agent, input.signature); + const points: Record = { + token_launch: 15, + trade_complete: 5, + governance_vote: 10, + }; + const earned = points[result.event_type] || 0; + return { + status: "success", + confirmed: result.confirmed, + event_type: result.event_type, + feedback_sent: result.feedback_sent, + message: `Transaction confirmed, +${earned} reputation`, + }; + } catch (error: any) { + return { + status: "error", + message: `Confirm failed: ${error.message}`, + }; + } + }, +}; + +// ============================================================================ +// V2.4: Treasury Lending Actions +// ============================================================================ + +export const torchGetLendingInfoAction: Action = { + name: "TORCH_GET_LENDING_INFO", + similes: [ + "torch lending info", + "check torch lending", + "is lending enabled on torch", + "torch treasury lending rates", + ], + description: + "Get lending state for a migrated Torch token. Returns interest rates, LTV limits, utilization, and active loan count. Lending is always enabled on migrated tokens.", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + interest_rate_bps: 200, + max_ltv_bps: 5000, + active_loans: 3, + }, + explanation: "Get current lending rates and utilization", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const info = await torchGetLendingInfo(agent, input.mint); + return { + status: "success", + ...info, + message: `Lending active: ${info.interest_rate_bps / 100}% per epoch, ${info.active_loans} loans, ${(info.total_sol_lent / 1e9).toFixed(2)} SOL lent`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get lending info: ${error.message}`, + }; + } + }, +}; + +export const torchGetLoanAction: Action = { + name: "TORCH_GET_LOAN", + similes: [ + "torch loan position", + "check my torch loan", + "torch loan health", + "am I liquidatable on torch", + ], + description: + "Get loan position details for a wallet on a Torch token. Shows collateral locked, SOL owed, accrued interest, current LTV, and health status (healthy/at_risk/liquidatable/none).", + examples: [ + [ + { + input: { mint: "ABC123..." }, + output: { + status: "success", + health: "healthy", + current_ltv_bps: 4020, + total_owed: 1005000000, + }, + explanation: "Check your loan position health", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + wallet: z + .string() + .optional() + .describe("Wallet to check (defaults to your wallet)"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const position = await torchGetLoanPosition(agent, input.mint, input.wallet); + if (position.health === "none") { + return { + status: "success", + ...position, + message: "No active loan position", + }; + } + return { + status: "success", + ...position, + message: `Loan: ${(position.total_owed / 1e9).toFixed(4)} SOL owed, LTV ${(position.current_ltv_bps / 100).toFixed(1)}%, ${position.health}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get loan position: ${error.message}`, + }; + } + }, +}; + +export const torchBorrowAction: Action = { + name: "TORCH_BORROW", + similes: [ + "borrow sol on torch", + "torch treasury borrow", + "lock tokens borrow sol", + "leverage torch tokens", + ], + description: + "Borrow SOL from a Torch token's treasury using tokens as collateral. Lock tokens in the collateral vault and receive SOL up to 50% of collateral value (max LTV). Token must be migrated and lending must be enabled. Note: 1% Token-2022 fee applies on collateral deposit.", + examples: [ + [ + { + input: { mint: "ABC123...", collateralTokens: 50000, solToBorrow: 1 }, + output: { + status: "success", + signature: "5xKp...", + message: "Borrowed 1 SOL with 50000 tokens as collateral", + }, + explanation: "Lock 50,000 tokens and borrow 1 SOL", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + collateralTokens: z + .number() + .min(0) + .describe("Tokens to lock as collateral (in whole tokens, not base units). Can be 0 if adding debt to existing position."), + solToBorrow: z + .number() + .min(0) + .describe("SOL to borrow. Can be 0 if just adding collateral."), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const collateralBaseUnits = Math.floor(input.collateralTokens * 1e6); + const borrowLamports = Math.floor(input.solToBorrow * 1e9); + const signature = await torchBorrowToken(agent, input.mint, collateralBaseUnits, borrowLamports); + return { + status: "success", + signature, + message: `Borrowed ${input.solToBorrow} SOL with ${input.collateralTokens.toLocaleString()} tokens as collateral`, + }; + } catch (error: any) { + return { + status: "error", + message: `Borrow failed: ${error.message}`, + }; + } + }, +}; + +export const torchRepayAction: Action = { + name: "TORCH_REPAY", + similes: [ + "repay torch loan", + "pay back torch borrow", + "close torch loan", + "return borrowed sol torch", + ], + description: + "Repay borrowed SOL on Torch Market. Interest is paid first, then principal. If you repay the full amount owed, all collateral tokens are returned to your wallet. Partial repay reduces debt but collateral stays locked.", + examples: [ + [ + { + input: { mint: "ABC123...", solAmount: 1.05 }, + output: { + status: "success", + signature: "5xKp...", + message: "Repaid 1.05 SOL", + }, + explanation: "Repay 1.05 SOL of borrowed debt", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + solAmount: z.number().positive().describe("SOL to repay"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const lamports = Math.floor(input.solAmount * 1e9); + const signature = await torchRepayLoan(agent, input.mint, lamports); + return { + status: "success", + signature, + message: `Repaid ${input.solAmount} SOL`, + }; + } catch (error: any) { + return { + status: "error", + message: `Repay failed: ${error.message}`, + }; + } + }, +}; + +export const torchLiquidateAction: Action = { + name: "TORCH_LIQUIDATE", + similes: [ + "liquidate torch loan", + "torch liquidation", + "liquidate underwater position torch", + ], + description: + "Liquidate an underwater loan position on Torch Market. Permissionless -- anyone can call when a borrower's LTV exceeds 65%. You pay SOL to the treasury and receive collateral tokens at a 10% bonus (profitable keeper operation).", + examples: [ + [ + { + input: { mint: "ABC123...", borrower: "BORROWER_WALLET..." }, + output: { + status: "success", + signature: "5xKp...", + message: "Liquidated position, received collateral + 10% bonus", + }, + explanation: "Liquidate an underwater loan for profit", + }, + ], + ], + schema: z.object({ + mint: z.string().describe("Token mint address"), + borrower: z.string().describe("Wallet address of the borrower to liquidate"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const signature = await torchLiquidateLoan(agent, input.mint, input.borrower); + return { + status: "success", + signature, + message: "Liquidated position, received collateral + 10% bonus", + }; + } catch (error: any) { + return { + status: "error", + message: `Liquidation failed: ${error.message}`, + }; + } + }, +}; diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts index 5ba494966..b6b13d102 100644 --- a/packages/plugin-defi/src/torch/tools/index.ts +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -3,6 +3,9 @@ import { type SolanaAgentKit, signOrSendTX } from "solana-agent-kit"; const TORCH_API = "https://torch.market/api/v1"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ApiResponse = { success: boolean; data?: any; error?: { message: string } }; + export interface TorchToken { mint: string; name: string; @@ -30,6 +33,13 @@ export interface TorchTokenDetail extends TorchToken { stars: number; } +export interface TorchMessage { + signature: string; + memo: string; + sender: string; + timestamp: number; +} + /** * List tokens on Torch Market * @param agent SolanaAgentKit instance @@ -50,7 +60,7 @@ export async function torchListTokens( if (limit) params.set("limit", limit.toString()); const res = await fetch(`${TORCH_API}/tokens?${params}`); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to list tokens"); return json.data.tokens; } @@ -66,7 +76,7 @@ export async function torchGetToken( mint: string, ): Promise { const res = await fetch(`${TORCH_API}/tokens/${mint}`); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Token not found"); return json.data; } @@ -77,6 +87,9 @@ export async function torchGetToken( * @param mint Token mint address * @param amountLamports Amount of SOL in lamports (1 SOL = 1e9 lamports) * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @param vote Vote on treasury outcome -- required on first buy, omit on subsequent buys. + * "burn" = destroy treasury tokens (deflationary), "return" = add to LP (deeper liquidity) + * @param message Optional message to bundle with the trade (SPL Memo, max 500 chars) * @returns Transaction signature */ export async function torchBuyToken( @@ -84,6 +97,8 @@ export async function torchBuyToken( mint: string, amountLamports: number, slippageBps: number = 100, + vote?: "burn" | "return", + message?: string, ) { const res = await fetch(`${TORCH_API}/transactions/buy`, { method: "POST", @@ -93,12 +108,25 @@ export async function torchBuyToken( buyer: agent.wallet.publicKey.toBase58(), amount_sol: amountLamports, slippage_bps: slippageBps, + ...(vote ? { vote } : {}), }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build buy transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + + // Bundle message as SPL Memo if provided + if (message) { + const { TransactionInstruction, PublicKey } = await import("@solana/web3.js"); + const MEMO_PROGRAM = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); + tx.add(new TransactionInstruction({ + programId: MEMO_PROGRAM, + keys: [{ pubkey: agent.wallet.publicKey, isSigner: true, isWritable: false }], + data: Buffer.from(message.slice(0, 500), "utf-8"), + })); + } + const { blockhash } = await agent.connection.getLatestBlockhash(); tx.recentBlockhash = blockhash; @@ -129,7 +157,7 @@ export async function torchSellToken( slippage_bps: slippageBps, }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build sell transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); @@ -144,8 +172,8 @@ export async function torchSellToken( * * After a token reaches 200 SOL, it graduates and holders vote on the * community treasury (10% of all tokens bought): - * - "burn": Destroy the tokens, reducing total supply - * - "return": Return the tokens to the token creator + * - "burn": Destroy the tokens, reducing total supply from 1B to 900M + * - "return": Add the tokens to Raydium LP for deeper liquidity * * @param agent SolanaAgentKit instance * @param mint Token mint address @@ -166,7 +194,7 @@ export async function torchVoteToken( vote, }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build vote transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); @@ -194,7 +222,7 @@ export async function torchStarToken( user: agent.wallet.publicKey.toBase58(), }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build star transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); @@ -204,13 +232,6 @@ export async function torchStarToken( return signOrSendTX(agent, tx); } -export interface TorchMessage { - signature: string; - memo: string; - sender: string; - timestamp: number; -} - /** * Create a new token on Torch Market with automatic bonding curve * @@ -242,7 +263,7 @@ export async function torchCreateToken( metadata_uri: metadataUri, }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build create transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); @@ -252,7 +273,7 @@ export async function torchCreateToken( const signature = await signOrSendTX(agent, tx); return { signature, - mint: json.data.mint, + mint: json.data.mint as string, }; } @@ -273,15 +294,15 @@ export async function torchGetMessages( if (limit) params.set("limit", limit.toString()); const res = await fetch(`${TORCH_API}/tokens/${mint}/messages?${params}`); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to get messages"); return json.data.messages; } /** * Post a message on a token's page - * AI agents can use this to communicate with each other - * Messages are stored on-chain as SPL Memos + * Messages are bundled with buy/sell transactions as SPL Memos -- + * every message has a provable trade behind it * @param agent SolanaAgentKit instance * @param mint Token mint address * @param message Message to post (max 500 characters) @@ -301,7 +322,7 @@ export async function torchPostMessage( message, }), }); - const json = await res.json(); + const json = await res.json() as ApiResponse; if (!json.success) throw new Error(json.error?.message || "Failed to build message transaction"); const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); @@ -310,3 +331,209 @@ export async function torchPostMessage( return signOrSendTX(agent, tx); } + +// ============================================================================ +// Treasury Lending Tools (V2.4) +// ============================================================================ + +export interface TorchLendingInfo { + interest_rate_bps: number; + max_ltv_bps: number; + liquidation_threshold_bps: number; + liquidation_bonus_bps: number; + total_sol_lent: number; + active_loans: number; + treasury_sol_available: number; +} + +export interface TorchLoanPosition { + collateral_amount: number; + borrowed_amount: number; + accrued_interest: number; + total_owed: number; + collateral_value_sol: number; + current_ltv_bps: number; + health: "healthy" | "at_risk" | "liquidatable" | "none"; +} + +/** + * Get lending configuration and state for a migrated token + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @returns Lending info including rates, caps, and active loan stats + */ +export async function torchGetLendingInfo( + agent: SolanaAgentKit, + mint: string, +): Promise { + const res = await fetch(`${TORCH_API}/lending/${mint}/info`); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to get lending info"); + return json.data; +} + +/** + * Get loan position for a wallet on a specific token + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param wallet Wallet address to check (defaults to agent's wallet) + * @returns Loan position details including collateral, debt, LTV, and health + */ +export async function torchGetLoanPosition( + agent: SolanaAgentKit, + mint: string, + wallet?: string, +): Promise { + const w = wallet || agent.wallet.publicKey.toBase58(); + const res = await fetch(`${TORCH_API}/lending/${mint}/position?wallet=${w}`); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to get loan position"); + return json.data; +} + +/** + * Borrow SOL from treasury using tokens as collateral + * + * Lock tokens in a collateral vault and receive SOL. The token must be + * migrated to Raydium and lending must be enabled. Max LTV is 50%. + * + * Note: Token-2022's 1% transfer fee applies when depositing collateral. + * + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param collateralAmount Tokens to lock as collateral (base units, 6 decimals). Can be 0 if adding debt only. + * @param solToBorrow SOL to borrow in lamports. Can be 0 if adding collateral only. + * @returns Transaction signature + */ +export async function torchBorrowToken( + agent: SolanaAgentKit, + mint: string, + collateralAmount: number, + solToBorrow: number, +) { + const res = await fetch(`${TORCH_API}/transactions/borrow`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + borrower: agent.wallet.publicKey.toBase58(), + collateral_amount: collateralAmount, + sol_to_borrow: solToBorrow, + }), + }); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to build borrow transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Repay borrowed SOL and receive collateral back + * + * Interest is paid first, then principal. If sol_amount >= total owed, + * this is a full repay and all collateral tokens are returned. + * + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param solAmount SOL to repay in lamports + * @returns Transaction signature + */ +export async function torchRepayLoan( + agent: SolanaAgentKit, + mint: string, + solAmount: number, +) { + const res = await fetch(`${TORCH_API}/transactions/repay`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + borrower: agent.wallet.publicKey.toBase58(), + sol_amount: solAmount, + }), + }); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to build repay transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +/** + * Liquidate an underwater loan position + * + * Permissionless -- anyone can call when a borrower's LTV exceeds the + * liquidation threshold (default 65%). Liquidator pays SOL to treasury + * and receives collateral tokens + 10% bonus. + * + * @param agent SolanaAgentKit instance + * @param mint Token mint address + * @param borrower Wallet address of the borrower to liquidate + * @returns Transaction signature + */ +export async function torchLiquidateLoan( + agent: SolanaAgentKit, + mint: string, + borrower: string, +) { + const res = await fetch(`${TORCH_API}/transactions/liquidate`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + mint, + liquidator: agent.wallet.publicKey.toBase58(), + borrower, + }), + }); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to build liquidate transaction"); + + const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const { blockhash } = await agent.connection.getLatestBlockhash(); + tx.recentBlockhash = blockhash; + + return signOrSendTX(agent, tx); +} + +export interface TorchConfirmResult { + confirmed: boolean; + event_type: "token_launch" | "trade_complete" | "governance_vote"; + feedback_sent: boolean; +} + +/** + * Confirm a transaction with SAID Protocol for reputation + * + * After a transaction is confirmed on-chain, call this to report + * success to SAID Protocol. This builds your trust score: + * - token_launch: +15 reputation + * - trade_complete: +5 reputation + * - governance_vote: +10 reputation + * + * @param agent SolanaAgentKit instance + * @param signature Transaction signature to confirm + * @returns Confirmation result with event type and reputation feedback status + */ +export async function torchConfirm( + agent: SolanaAgentKit, + signature: string, +): Promise { + const res = await fetch(`${TORCH_API}/confirm`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + signature, + wallet: agent.wallet.publicKey.toBase58(), + }), + }); + const json = await res.json() as ApiResponse; + if (!json.success) throw new Error(json.error?.message || "Failed to confirm transaction"); + return json.data; +} From 1bc608a3ff9b27fe974027a6c35719755233bf0b Mon Sep 17 00:00:00 2001 From: Sir Date: Sun, 8 Feb 2026 14:07:28 -0500 Subject: [PATCH 5/6] update to match agent kit v3.0 on npm --- packages/plugin-defi/package.json | 1 + .../plugin-defi/src/torch/actions/index.ts | 98 ++-- packages/plugin-defi/src/torch/tools/index.ts | 479 +++++++----------- pnpm-lock.yaml | 67 +++ 4 files changed, 287 insertions(+), 358 deletions(-) diff --git a/packages/plugin-defi/package.json b/packages/plugin-defi/package.json index a60e803ca..7734f1e68 100644 --- a/packages/plugin-defi/package.json +++ b/packages/plugin-defi/package.json @@ -58,6 +58,7 @@ "redaxios": "^0.5.1", "rpc-websockets": "^10.0.0", "solana-agent-kit": "workspace:*", + "torchsdk": "^1.0.0", "zod": "^3.24.1" }, "peerDependencies": { diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts index 1a4d1dd83..7676e8e18 100644 --- a/packages/plugin-defi/src/torch/actions/index.ts +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -47,16 +47,8 @@ export const torchListTokensAction: Action = { .enum(["bonding", "complete", "migrated", "all"]) .optional() .describe("Filter by token status"), - sort: z - .enum(["newest", "volume", "marketcap"]) - .optional() - .describe("Sort order"), - limit: z - .number() - .positive() - .max(100) - .optional() - .describe("Number of tokens to return"), + sort: z.enum(["newest", "volume", "marketcap"]).optional().describe("Sort order"), + limit: z.number().positive().max(100).optional().describe("Number of tokens to return"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -164,18 +156,29 @@ export const torchBuyTokenAction: Action = { vote: z .enum(["burn", "return"]) .optional() - .describe("Treasury vote -- REQUIRED on first buy. 'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP"), + .describe( + "Treasury vote -- REQUIRED on first buy. 'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP", + ), message: z .string() .max(500) .optional() - .describe("Optional message to bundle as on-chain SPL Memo (max 500 chars). Skin-in-the-game: every message has a provable trade behind it."), + .describe( + "Optional message to bundle as on-chain SPL Memo (max 500 chars). Skin-in-the-game: every message has a provable trade behind it.", + ), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { const lamports = Math.floor(input.amountSol * 1e9); const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; - const signature = await torchBuyToken(agent, input.mint, lamports, bps, input.vote, input.message); + const signature = await torchBuyToken( + agent, + input.mint, + lamports, + bps, + input.vote, + input.message, + ); const parts = [`Bought tokens for ${input.amountSol} SOL`]; if (input.vote) parts.push(`(voted: ${input.vote})`); if (input.message) parts.push("with message"); @@ -195,11 +198,7 @@ export const torchBuyTokenAction: Action = { export const torchSellTokenAction: Action = { name: "TORCH_SELL_TOKEN", - similes: [ - "sell token on torch", - "sell torch token", - "exit torch position", - ], + similes: ["sell token on torch", "sell torch token", "exit torch position"], description: "Sell tokens back to Torch Market bonding curve. No sell fees. Specify amount in tokens.", examples: [ @@ -246,12 +245,7 @@ export const torchSellTokenAction: Action = { export const torchVoteTokenAction: Action = { name: "TORCH_VOTE_TOKEN", - similes: [ - "vote on torch token", - "torch treasury vote", - "vote burn torch", - "vote return torch", - ], + similes: ["vote on torch token", "torch treasury vote", "vote burn torch", "vote return torch"], description: "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply from 1B to 900M), 'return' adds them to the Raydium LP for deeper liquidity. One wallet, one vote. The result is binding and executed at migration.", examples: [ @@ -271,12 +265,17 @@ export const torchVoteTokenAction: Action = { mint: z.string().describe("Token mint address"), vote: z .enum(["burn", "return"]) - .describe("'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP (deeper liquidity)"), + .describe( + "'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP (deeper liquidity)", + ), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { const signature = await torchVoteToken(agent, input.mint, input.vote); - const desc = input.vote === "burn" ? "burn treasury tokens (reduce supply)" : "return tokens to LP (deepen liquidity)"; + const desc = + input.vote === "burn" + ? "burn treasury tokens (reduce supply)" + : "return tokens to LP (deepen liquidity)"; return { status: "success", signature, @@ -294,11 +293,7 @@ export const torchVoteTokenAction: Action = { export const torchStarTokenAction: Action = { name: "TORCH_STAR_TOKEN", - similes: [ - "star torch token", - "support torch token", - "like token on torch", - ], + similes: ["star torch token", "support torch token", "like token on torch"], description: "Star a token on Torch Market to signal sybil-resistant support (costs 0.05 SOL). When tokens reach 2000 stars, creators receive the accumulated ~100 SOL.", examples: [ @@ -367,16 +362,14 @@ export const torchCreateTokenAction: Action = { schema: z.object({ name: z.string().max(32).describe("Token name (max 32 characters)"), symbol: z.string().max(10).describe("Token symbol (max 10 characters)"), - metadataUri: z.string().url().describe("URI pointing to token metadata JSON with name, symbol, description, and image"), + metadataUri: z + .string() + .url() + .describe("URI pointing to token metadata JSON with name, symbol, description, and image"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { - const result = await torchCreateToken( - agent, - input.name, - input.symbol, - input.metadataUri, - ); + const result = await torchCreateToken(agent, input.name, input.symbol, input.metadataUri); return { status: "success", signature: result.signature, @@ -541,7 +534,7 @@ export const torchConfirmAction: Action = { }; // ============================================================================ -// V2.4: Treasury Lending Actions +// Treasury Lending Actions // ============================================================================ export const torchGetLendingInfoAction: Action = { @@ -614,10 +607,7 @@ export const torchGetLoanAction: Action = { ], schema: z.object({ mint: z.string().describe("Token mint address"), - wallet: z - .string() - .optional() - .describe("Wallet to check (defaults to your wallet)"), + wallet: z.string().optional().describe("Wallet to check (defaults to your wallet)"), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { @@ -671,17 +661,21 @@ export const torchBorrowAction: Action = { collateralTokens: z .number() .min(0) - .describe("Tokens to lock as collateral (in whole tokens, not base units). Can be 0 if adding debt to existing position."), - solToBorrow: z - .number() - .min(0) - .describe("SOL to borrow. Can be 0 if just adding collateral."), + .describe( + "Tokens to lock as collateral (in whole tokens, not base units). Can be 0 if adding debt to existing position.", + ), + solToBorrow: z.number().min(0).describe("SOL to borrow. Can be 0 if just adding collateral."), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { const collateralBaseUnits = Math.floor(input.collateralTokens * 1e6); const borrowLamports = Math.floor(input.solToBorrow * 1e9); - const signature = await torchBorrowToken(agent, input.mint, collateralBaseUnits, borrowLamports); + const signature = await torchBorrowToken( + agent, + input.mint, + collateralBaseUnits, + borrowLamports, + ); return { status: "success", signature, @@ -743,11 +737,7 @@ export const torchRepayAction: Action = { export const torchLiquidateAction: Action = { name: "TORCH_LIQUIDATE", - similes: [ - "liquidate torch loan", - "torch liquidation", - "liquidate underwater position torch", - ], + similes: ["liquidate torch loan", "torch liquidation", "liquidate underwater position torch"], description: "Liquidate an underwater loan position on Torch Market. Permissionless -- anyone can call when a borrower's LTV exceeds 65%. You pay SOL to the treasury and receive collateral tokens at a 10% bonus (profitable keeper operation).", examples: [ diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts index b6b13d102..ede92a424 100644 --- a/packages/plugin-defi/src/torch/tools/index.ts +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -1,44 +1,44 @@ -import { Transaction } from "@solana/web3.js"; +import { TransactionInstruction, PublicKey } from "@solana/web3.js"; import { type SolanaAgentKit, signOrSendTX } from "solana-agent-kit"; +import { + getTokens, + getToken, + getMessages, + getLendingInfo, + getLoanPosition, + buildBuyTransaction, + buildSellTransaction, + buildCreateTokenTransaction, + buildVoteTransaction, + buildStarTransaction, + buildMessageTransaction, + buildBorrowTransaction, + buildRepayTransaction, + buildLiquidateTransaction, + confirmTransaction, +} from "torchsdk"; +import type { + TokenSummary, + TokenDetail, + TokenMessage, + LendingInfo, + LoanPositionInfo, +} from "torchsdk"; + +// Re-export SDK types with Torch-prefixed names for backwards compatibility +export type TorchToken = TokenSummary; +export type TorchTokenDetail = TokenDetail; +export type TorchMessage = TokenMessage; +export type TorchLendingInfo = LendingInfo; +export type TorchLoanPosition = LoanPositionInfo; -const TORCH_API = "https://torch.market/api/v1"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ApiResponse = { success: boolean; data?: any; error?: { message: string } }; - -export interface TorchToken { - mint: string; - name: string; - symbol: string; - status: "bonding" | "complete" | "migrated"; - price_sol: number; - market_cap_sol: number; - progress_percent: number; - holders: number; - created_at: number; -} - -export interface TorchTokenDetail extends TorchToken { - description?: string; - image?: string; - sol_raised: number; - sol_target: number; - total_supply: number; - circulating_supply: number; - treasury_sol_balance: number; - treasury_token_balance: number; - votes_return: number; - votes_burn: number; - creator: string; - stars: number; +export interface TorchConfirmResult { + confirmed: boolean; + event_type: "token_launch" | "trade_complete" | "governance_vote" | "unknown"; + feedback_sent: boolean; } -export interface TorchMessage { - signature: string; - memo: string; - sender: string; - timestamp: number; -} +const SAID_API_URL = "https://api.saidprotocol.com/api"; /** * List tokens on Torch Market @@ -48,22 +48,19 @@ export interface TorchMessage { * @param limit Number of tokens to return (max 100) * @returns Array of token summaries */ -export async function torchListTokens( +export const torchListTokens = async ( agent: SolanaAgentKit, status?: "bonding" | "complete" | "migrated" | "all", sort?: "newest" | "volume" | "marketcap", limit?: number, -): Promise { - const params = new URLSearchParams(); - if (status) params.set("status", status); - if (sort) params.set("sort", sort); - if (limit) params.set("limit", limit.toString()); - - const res = await fetch(`${TORCH_API}/tokens?${params}`); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to list tokens"); - return json.data.tokens; -} +): Promise => { + const result = await getTokens(agent.connection, { + status: status || "all", + sort, + limit, + }); + return result.tokens; +}; /** * Get detailed information about a token @@ -71,15 +68,12 @@ export async function torchListTokens( * @param mint Token mint address * @returns Token details including treasury state and votes */ -export async function torchGetToken( +export const torchGetToken = async ( agent: SolanaAgentKit, mint: string, -): Promise { - const res = await fetch(`${TORCH_API}/tokens/${mint}`); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Token not found"); - return json.data; -} +): Promise => { + return getToken(agent.connection, mint); +}; /** * Buy tokens on Torch Market bonding curve @@ -92,46 +86,38 @@ export async function torchGetToken( * @param message Optional message to bundle with the trade (SPL Memo, max 500 chars) * @returns Transaction signature */ -export async function torchBuyToken( +export const torchBuyToken = async ( agent: SolanaAgentKit, mint: string, amountLamports: number, slippageBps: number = 100, vote?: "burn" | "return", message?: string, -) { - const res = await fetch(`${TORCH_API}/transactions/buy`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - buyer: agent.wallet.publicKey.toBase58(), - amount_sol: amountLamports, - slippage_bps: slippageBps, - ...(vote ? { vote } : {}), - }), +) => { + const result = await buildBuyTransaction(agent.connection, { + mint, + buyer: agent.wallet.publicKey.toBase58(), + amount_sol: amountLamports, + slippage_bps: slippageBps, + ...(vote ? { vote } : {}), }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build buy transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); + const tx = result.transaction; // Bundle message as SPL Memo if provided if (message) { - const { TransactionInstruction, PublicKey } = await import("@solana/web3.js"); const MEMO_PROGRAM = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); - tx.add(new TransactionInstruction({ - programId: MEMO_PROGRAM, - keys: [{ pubkey: agent.wallet.publicKey, isSigner: true, isWritable: false }], - data: Buffer.from(message.slice(0, 500), "utf-8"), - })); + tx.add( + new TransactionInstruction({ + programId: MEMO_PROGRAM, + keys: [{ pubkey: agent.wallet.publicKey, isSigner: true, isWritable: false }], + data: Buffer.from(message.slice(0, 500), "utf-8"), + }), + ); } - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - return signOrSendTX(agent, tx); -} +}; /** * Sell tokens back to Torch Market bonding curve @@ -141,31 +127,21 @@ export async function torchBuyToken( * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) * @returns Transaction signature */ -export async function torchSellToken( +export const torchSellToken = async ( agent: SolanaAgentKit, mint: string, amountTokens: number, slippageBps: number = 100, -) { - const res = await fetch(`${TORCH_API}/transactions/sell`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - seller: agent.wallet.publicKey.toBase58(), - amount_tokens: amountTokens, - slippage_bps: slippageBps, - }), +) => { + const result = await buildSellTransaction(agent.connection, { + mint, + seller: agent.wallet.publicKey.toBase58(), + amount_tokens: amountTokens, + slippage_bps: slippageBps, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build sell transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; /** * Vote on treasury outcome for a graduated token @@ -180,29 +156,19 @@ export async function torchSellToken( * @param vote Vote choice: "burn" or "return" * @returns Transaction signature */ -export async function torchVoteToken( +export const torchVoteToken = async ( agent: SolanaAgentKit, mint: string, vote: "burn" | "return", -) { - const res = await fetch(`${TORCH_API}/transactions/vote`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - voter: agent.wallet.publicKey.toBase58(), - vote, - }), +) => { + const result = await buildVoteTransaction(agent.connection, { + mint, + voter: agent.wallet.publicKey.toBase58(), + vote, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build vote transaction"); - - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; /** * Star a token to show support (costs 0.05 SOL) @@ -210,27 +176,14 @@ export async function torchVoteToken( * @param mint Token mint address * @returns Transaction signature */ -export async function torchStarToken( - agent: SolanaAgentKit, - mint: string, -) { - const res = await fetch(`${TORCH_API}/transactions/star`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - user: agent.wallet.publicKey.toBase58(), - }), +export const torchStarToken = async (agent: SolanaAgentKit, mint: string) => { + const result = await buildStarTransaction(agent.connection, { + mint, + user: agent.wallet.publicKey.toBase58(), }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build star transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; /** * Create a new token on Torch Market with automatic bonding curve @@ -247,35 +200,25 @@ export async function torchStarToken( * @param metadataUri URI pointing to token metadata JSON (Metaplex standard) * @returns Transaction signature and new token mint address */ -export async function torchCreateToken( +export const torchCreateToken = async ( agent: SolanaAgentKit, name: string, symbol: string, metadataUri: string, -) { - const res = await fetch(`${TORCH_API}/transactions/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - creator: agent.wallet.publicKey.toBase58(), - name, - symbol, - metadata_uri: metadataUri, - }), +) => { + const result = await buildCreateTokenTransaction(agent.connection, { + creator: agent.wallet.publicKey.toBase58(), + name, + symbol, + metadata_uri: metadataUri, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build create transaction"); - - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - const signature = await signOrSendTX(agent, tx); + const signature = await signOrSendTX(agent, result.transaction); return { signature, - mint: json.data.mint as string, + mint: result.mint.toBase58(), }; -} +}; /** * Get messages (memos) from a token's page @@ -285,19 +228,14 @@ export async function torchCreateToken( * @param limit Number of messages to return (max 100) * @returns Array of messages */ -export async function torchGetMessages( +export const torchGetMessages = async ( agent: SolanaAgentKit, mint: string, limit: number = 50, -): Promise { - const params = new URLSearchParams(); - if (limit) params.set("limit", limit.toString()); - - const res = await fetch(`${TORCH_API}/tokens/${mint}/messages?${params}`); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to get messages"); - return json.data.messages; -} +): Promise => { + const result = await getMessages(agent.connection, mint, limit); + return result.messages; +}; /** * Post a message on a token's page @@ -308,69 +246,32 @@ export async function torchGetMessages( * @param message Message to post (max 500 characters) * @returns Transaction signature */ -export async function torchPostMessage( - agent: SolanaAgentKit, - mint: string, - message: string, -) { - const res = await fetch(`${TORCH_API}/transactions/message`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - sender: agent.wallet.publicKey.toBase58(), - message, - }), +export const torchPostMessage = async (agent: SolanaAgentKit, mint: string, message: string) => { + const result = await buildMessageTransaction(agent.connection, { + mint, + sender: agent.wallet.publicKey.toBase58(), + message, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build message transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; // ============================================================================ -// Treasury Lending Tools (V2.4) +// Treasury Lending Tools // ============================================================================ -export interface TorchLendingInfo { - interest_rate_bps: number; - max_ltv_bps: number; - liquidation_threshold_bps: number; - liquidation_bonus_bps: number; - total_sol_lent: number; - active_loans: number; - treasury_sol_available: number; -} - -export interface TorchLoanPosition { - collateral_amount: number; - borrowed_amount: number; - accrued_interest: number; - total_owed: number; - collateral_value_sol: number; - current_ltv_bps: number; - health: "healthy" | "at_risk" | "liquidatable" | "none"; -} - /** * Get lending configuration and state for a migrated token * @param agent SolanaAgentKit instance * @param mint Token mint address * @returns Lending info including rates, caps, and active loan stats */ -export async function torchGetLendingInfo( +export const torchGetLendingInfo = async ( agent: SolanaAgentKit, mint: string, -): Promise { - const res = await fetch(`${TORCH_API}/lending/${mint}/info`); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to get lending info"); - return json.data; -} +): Promise => { + return getLendingInfo(agent.connection, mint); +}; /** * Get loan position for a wallet on a specific token @@ -379,17 +280,14 @@ export async function torchGetLendingInfo( * @param wallet Wallet address to check (defaults to agent's wallet) * @returns Loan position details including collateral, debt, LTV, and health */ -export async function torchGetLoanPosition( +export const torchGetLoanPosition = async ( agent: SolanaAgentKit, mint: string, wallet?: string, -): Promise { +): Promise => { const w = wallet || agent.wallet.publicKey.toBase58(); - const res = await fetch(`${TORCH_API}/lending/${mint}/position?wallet=${w}`); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to get loan position"); - return json.data; -} + return getLoanPosition(agent.connection, mint, w); +}; /** * Borrow SOL from treasury using tokens as collateral @@ -405,31 +303,21 @@ export async function torchGetLoanPosition( * @param solToBorrow SOL to borrow in lamports. Can be 0 if adding collateral only. * @returns Transaction signature */ -export async function torchBorrowToken( +export const torchBorrowToken = async ( agent: SolanaAgentKit, mint: string, collateralAmount: number, solToBorrow: number, -) { - const res = await fetch(`${TORCH_API}/transactions/borrow`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - borrower: agent.wallet.publicKey.toBase58(), - collateral_amount: collateralAmount, - sol_to_borrow: solToBorrow, - }), +) => { + const result = await buildBorrowTransaction(agent.connection, { + mint, + borrower: agent.wallet.publicKey.toBase58(), + collateral_amount: collateralAmount, + sol_to_borrow: solToBorrow, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build borrow transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; /** * Repay borrowed SOL and receive collateral back @@ -442,29 +330,15 @@ export async function torchBorrowToken( * @param solAmount SOL to repay in lamports * @returns Transaction signature */ -export async function torchRepayLoan( - agent: SolanaAgentKit, - mint: string, - solAmount: number, -) { - const res = await fetch(`${TORCH_API}/transactions/repay`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - borrower: agent.wallet.publicKey.toBase58(), - sol_amount: solAmount, - }), +export const torchRepayLoan = async (agent: SolanaAgentKit, mint: string, solAmount: number) => { + const result = await buildRepayTransaction(agent.connection, { + mint, + borrower: agent.wallet.publicKey.toBase58(), + sol_amount: solAmount, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build repay transaction"); - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - - return signOrSendTX(agent, tx); -} + return signOrSendTX(agent, result.transaction); +}; /** * Liquidate an underwater loan position @@ -478,35 +352,15 @@ export async function torchRepayLoan( * @param borrower Wallet address of the borrower to liquidate * @returns Transaction signature */ -export async function torchLiquidateLoan( - agent: SolanaAgentKit, - mint: string, - borrower: string, -) { - const res = await fetch(`${TORCH_API}/transactions/liquidate`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - mint, - liquidator: agent.wallet.publicKey.toBase58(), - borrower, - }), +export const torchLiquidateLoan = async (agent: SolanaAgentKit, mint: string, borrower: string) => { + const result = await buildLiquidateTransaction(agent.connection, { + mint, + liquidator: agent.wallet.publicKey.toBase58(), + borrower, }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to build liquidate transaction"); - - const tx = Transaction.from(Buffer.from(json.data.transaction, "base64")); - const { blockhash } = await agent.connection.getLatestBlockhash(); - tx.recentBlockhash = blockhash; - return signOrSendTX(agent, tx); -} - -export interface TorchConfirmResult { - confirmed: boolean; - event_type: "token_launch" | "trade_complete" | "governance_vote"; - feedback_sent: boolean; -} + return signOrSendTX(agent, result.transaction); +}; /** * Confirm a transaction with SAID Protocol for reputation @@ -521,19 +375,36 @@ export interface TorchConfirmResult { * @param signature Transaction signature to confirm * @returns Confirmation result with event type and reputation feedback status */ -export async function torchConfirm( +export const torchConfirm = async ( agent: SolanaAgentKit, signature: string, -): Promise { - const res = await fetch(`${TORCH_API}/confirm`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - signature, - wallet: agent.wallet.publicKey.toBase58(), - }), - }); - const json = await res.json() as ApiResponse; - if (!json.success) throw new Error(json.error?.message || "Failed to confirm transaction"); - return json.data; -} +): Promise => { + const wallet = agent.wallet.publicKey.toBase58(); + + // Confirm on-chain via SDK (reads RPC directly) + const result = await confirmTransaction(agent.connection, signature, wallet); + + // Send feedback to SAID Protocol for reputation + let feedbackSent = false; + try { + const saidRes = await fetch(`${SAID_API_URL}/feedback`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + wallet, + signature, + event_type: result.event_type, + success: true, + }), + }); + feedbackSent = saidRes.ok; + } catch { + // SAID feedback is best-effort + } + + return { + confirmed: result.confirmed, + event_type: result.event_type, + feedback_sent: feedbackSent, + }; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e618a61ca..01f8c6560 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,6 +264,9 @@ importers: solana-agent-kit: specifier: workspace:* version: link:../core + torchsdk: + specifier: ^1.0.0 + version: 1.0.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) zod: specifier: ^3.24.1 version: 3.24.1 @@ -811,6 +814,10 @@ packages: resolution: {integrity: sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==} engines: {node: '>=17'} + '@coral-xyz/anchor@0.32.1': + resolution: {integrity: sha512-zAyxFtfeje2FbMA1wzgcdVs7Hng/MijPKpRijoySPCicnvcTQs/+dnPZ/cR+LcXM9v9UYSyW81uRNYZtN5G4yg==} + engines: {node: '>=17'} + '@coral-xyz/borsh@0.26.0': resolution: {integrity: sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==} engines: {node: '>=10'} @@ -2757,6 +2764,12 @@ packages: peerDependencies: '@solana/web3.js': ^1.95.5 + '@solana/spl-token@0.4.14': + resolution: {integrity: sha512-u09zr96UBpX4U685MnvQsNzlvw9TiY005hk1vJmJr7gMJldoPG1eYU5/wNEyOA5lkMLiR/gOi9SFD4MefOYEsA==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.5 + '@solana/spl-token@0.4.6': resolution: {integrity: sha512-1nCnUqfHVtdguFciVWaY/RKcQz1IF4b31jnKgAmjU9QVN1q7dRUkTEWJZgTYIEtsULjVnC9jRqlhgGN39WbKKA==} engines: {node: '>=16'} @@ -6230,6 +6243,11 @@ packages: toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + torchsdk@1.0.0: + resolution: {integrity: sha512-c3gaRIPZ3XPbfk71ufjWoP8eEGwkiRN8JEEmcqgqvtn2eoDuq9xEpRTlU+iEuJkS8kMoRFBWYzMp6E5KFLbH+g==} + peerDependencies: + '@solana/web3.js': ^1.98.0 + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -7522,6 +7540,27 @@ snapshots: - typescript - utf-8-validate + '@coral-xyz/anchor@0.32.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor-errors': 0.31.1 + '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.7.2 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0(encoding@0.1.13) + eventemitter3: 4.0.7 + pako: 2.1.0 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + '@coral-xyz/borsh@0.26.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) @@ -11068,6 +11107,21 @@ snapshots: - typescript - utf-8-validate + '@solana/spl-token@0.4.14(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) + buffer: 6.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@solana/spl-token@0.4.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -15497,6 +15551,19 @@ snapshots: toml@3.0.0: {} + torchsdk@1.0.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10): + dependencies: + '@coral-xyz/anchor': 0.32.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.8.3)(utf-8-validate@5.0.10) + bs58: 6.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + tr46@0.0.3: {} tr46@1.0.1: From 34958aa53616a8f637d46bcaf40c21b71c836491 Mon Sep 17 00:00:00 2001 From: Sir Date: Sun, 8 Feb 2026 22:42:47 -0500 Subject: [PATCH 6/6] update to latest torchsdk 1.0.4 and latest agent kit instructions. --- packages/plugin-defi/package.json | 2 +- packages/plugin-defi/src/index.ts | 8 -- .../plugin-defi/src/torch/actions/index.ts | 121 ++++-------------- packages/plugin-defi/src/torch/tools/index.ts | 69 +--------- 4 files changed, 29 insertions(+), 171 deletions(-) diff --git a/packages/plugin-defi/package.json b/packages/plugin-defi/package.json index 7734f1e68..470ded66a 100644 --- a/packages/plugin-defi/package.json +++ b/packages/plugin-defi/package.json @@ -58,7 +58,7 @@ "redaxios": "^0.5.1", "rpc-websockets": "^10.0.0", "solana-agent-kit": "workspace:*", - "torchsdk": "^1.0.0", + "torchsdk": "1.0.4", "zod": "^3.24.1" }, "peerDependencies": { diff --git a/packages/plugin-defi/src/index.ts b/packages/plugin-defi/src/index.ts index c29e073ae..5c3d9ed6c 100644 --- a/packages/plugin-defi/src/index.ts +++ b/packages/plugin-defi/src/index.ts @@ -225,11 +225,9 @@ import { torchGetToken, torchBuyToken, torchSellToken, - torchVoteToken, torchStarToken, torchCreateToken, torchGetMessages, - torchPostMessage, torchConfirm, torchGetLendingInfo, torchGetLoanPosition, @@ -242,11 +240,9 @@ import { torchGetTokenAction, torchBuyTokenAction, torchSellTokenAction, - torchVoteTokenAction, torchStarTokenAction, torchCreateTokenAction, torchGetMessagesAction, - torchPostMessageAction, torchConfirmAction, torchGetLendingInfoAction, torchGetLoanAction, @@ -380,11 +376,9 @@ const DefiPlugin = { torchGetToken, torchBuyToken, torchSellToken, - torchVoteToken, torchStarToken, torchCreateToken, torchGetMessages, - torchPostMessage, torchConfirm, torchGetLendingInfo, torchGetLoanPosition, @@ -502,11 +496,9 @@ const DefiPlugin = { torchGetTokenAction, torchBuyTokenAction, torchSellTokenAction, - torchVoteTokenAction, torchStarTokenAction, torchCreateTokenAction, torchGetMessagesAction, - torchPostMessageAction, torchConfirmAction, torchGetLendingInfoAction, torchGetLoanAction, diff --git a/packages/plugin-defi/src/torch/actions/index.ts b/packages/plugin-defi/src/torch/actions/index.ts index 7676e8e18..ea41e4f45 100644 --- a/packages/plugin-defi/src/torch/actions/index.ts +++ b/packages/plugin-defi/src/torch/actions/index.ts @@ -5,11 +5,9 @@ import { torchGetToken, torchBuyToken, torchSellToken, - torchVoteToken, torchStarToken, torchCreateToken, torchGetMessages, - torchPostMessage, torchConfirm, torchGetLendingInfo, torchGetLoanPosition, @@ -200,7 +198,7 @@ export const torchSellTokenAction: Action = { name: "TORCH_SELL_TOKEN", similes: ["sell token on torch", "sell torch token", "exit torch position"], description: - "Sell tokens back to Torch Market bonding curve. No sell fees. Specify amount in tokens.", + "Sell tokens back to Torch Market bonding curve. No sell fees. Specify amount in tokens. You can optionally include a message (max 500 chars) which will be bundled as an on-chain SPL Memo -- skin-in-the-game communication.", examples: [ [ { @@ -213,79 +211,51 @@ export const torchSellTokenAction: Action = { explanation: "Sell 1M tokens back to the bonding curve", }, ], - ], - schema: z.object({ - mint: z.string().describe("Token mint address"), - amountTokens: z.number().positive().describe("Amount of tokens to sell"), - slippagePercent: z - .number() - .positive() - .max(50) - .optional() - .describe("Slippage tolerance as percentage (default 1%)"), - }), - handler: async (agent: SolanaAgentKit, input: Record) => { - try { - const baseUnits = Math.floor(input.amountTokens * 1e6); - const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; - const signature = await torchSellToken(agent, input.mint, baseUnits, bps); - return { - status: "success", - signature, - message: `Sold ${input.amountTokens.toLocaleString()} tokens`, - }; - } catch (error: any) { - return { - status: "error", - message: `Sell failed: ${error.message}`, - }; - } - }, -}; - -export const torchVoteTokenAction: Action = { - name: "TORCH_VOTE_TOKEN", - similes: ["vote on torch token", "torch treasury vote", "vote burn torch", "vote return torch"], - description: - "Vote on treasury outcome for a graduated Torch token. After reaching 200 SOL, holders vote: 'burn' destroys treasury tokens (reducing supply from 1B to 900M), 'return' adds them to the Raydium LP for deeper liquidity. One wallet, one vote. The result is binding and executed at migration.", - examples: [ [ { - input: { mint: "ABC123...", vote: "burn" }, + input: { mint: "ABC123...", amountTokens: 500000, message: "Taking profits, gl everyone" }, output: { status: "success", signature: "5xKp...", - message: "Voted to burn treasury tokens", + message: "Sold 500K tokens with message", }, - explanation: "Vote to burn the community treasury tokens", + explanation: "Sell tokens with an on-chain message bundled in", }, ], ], schema: z.object({ mint: z.string().describe("Token mint address"), - vote: z - .enum(["burn", "return"]) + amountTokens: z.number().positive().describe("Amount of tokens to sell"), + slippagePercent: z + .number() + .positive() + .max(50) + .optional() + .describe("Slippage tolerance as percentage (default 1%)"), + message: z + .string() + .max(500) + .optional() .describe( - "'burn' = destroy treasury tokens (deflationary), 'return' = add to Raydium LP (deeper liquidity)", + "Optional message to bundle as on-chain SPL Memo (max 500 chars). Skin-in-the-game: every message has a provable trade behind it.", ), }), handler: async (agent: SolanaAgentKit, input: Record) => { try { - const signature = await torchVoteToken(agent, input.mint, input.vote); - const desc = - input.vote === "burn" - ? "burn treasury tokens (reduce supply)" - : "return tokens to LP (deepen liquidity)"; + const baseUnits = Math.floor(input.amountTokens * 1e6); + const bps = input.slippagePercent ? Math.floor(input.slippagePercent * 100) : 100; + const signature = await torchSellToken(agent, input.mint, baseUnits, bps, input.message); + const parts = [`Sold ${input.amountTokens.toLocaleString()} tokens`]; + if (input.message) parts.push("with message"); return { status: "success", signature, - vote: input.vote, - message: `Voted to ${desc}`, + message: parts.join(" "), }; } catch (error: any) { return { status: "error", - message: `Vote failed: ${error.message}`, + message: `Sell failed: ${error.message}`, }; } }, @@ -436,51 +406,6 @@ export const torchGetMessagesAction: Action = { }, }; -export const torchPostMessageAction: Action = { - name: "TORCH_POST_MESSAGE", - similes: [ - "post torch message", - "send torch message", - "say something on torch", - "communicate on torch", - "message other agents", - ], - description: - "Post a standalone message on a token's page on Torch Market. Prefer using the 'message' parameter on TORCH_BUY_TOKEN instead -- bundling a message with a trade is the preferred approach (skin-in-the-game). This standalone endpoint is for cases where you need to post without a trade.", - examples: [ - [ - { - input: { mint: "ABC123...", message: "Hello from an AI agent!" }, - output: { - status: "success", - signature: "5xKp...", - message: "Posted message", - }, - explanation: "Post a message on a token's page", - }, - ], - ], - schema: z.object({ - mint: z.string().describe("Token mint address"), - message: z.string().max(500).describe("Message to post (max 500 characters)"), - }), - handler: async (agent: SolanaAgentKit, input: Record) => { - try { - const signature = await torchPostMessage(agent, input.mint, input.message); - return { - status: "success", - signature, - message: "Posted message", - }; - } catch (error: any) { - return { - status: "error", - message: `Post message failed: ${error.message}`, - }; - } - }, -}; - export const torchConfirmAction: Action = { name: "TORCH_CONFIRM", similes: [ diff --git a/packages/plugin-defi/src/torch/tools/index.ts b/packages/plugin-defi/src/torch/tools/index.ts index ede92a424..9969ff7e9 100644 --- a/packages/plugin-defi/src/torch/tools/index.ts +++ b/packages/plugin-defi/src/torch/tools/index.ts @@ -1,4 +1,3 @@ -import { TransactionInstruction, PublicKey } from "@solana/web3.js"; import { type SolanaAgentKit, signOrSendTX } from "solana-agent-kit"; import { getTokens, @@ -9,9 +8,7 @@ import { buildBuyTransaction, buildSellTransaction, buildCreateTokenTransaction, - buildVoteTransaction, buildStarTransaction, - buildMessageTransaction, buildBorrowTransaction, buildRepayTransaction, buildLiquidateTransaction, @@ -100,23 +97,10 @@ export const torchBuyToken = async ( amount_sol: amountLamports, slippage_bps: slippageBps, ...(vote ? { vote } : {}), + ...(message ? { message } : {}), }); - const tx = result.transaction; - - // Bundle message as SPL Memo if provided - if (message) { - const MEMO_PROGRAM = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); - tx.add( - new TransactionInstruction({ - programId: MEMO_PROGRAM, - keys: [{ pubkey: agent.wallet.publicKey, isSigner: true, isWritable: false }], - data: Buffer.from(message.slice(0, 500), "utf-8"), - }), - ); - } - - return signOrSendTX(agent, tx); + return signOrSendTX(agent, result.transaction); }; /** @@ -125,6 +109,7 @@ export const torchBuyToken = async ( * @param mint Token mint address * @param amountTokens Amount of tokens in base units (6 decimals) * @param slippageBps Slippage tolerance in basis points (default 100 = 1%) + * @param message Optional message to bundle with the trade (SPL Memo, max 500 chars) * @returns Transaction signature */ export const torchSellToken = async ( @@ -132,39 +117,14 @@ export const torchSellToken = async ( mint: string, amountTokens: number, slippageBps: number = 100, + message?: string, ) => { const result = await buildSellTransaction(agent.connection, { mint, seller: agent.wallet.publicKey.toBase58(), amount_tokens: amountTokens, slippage_bps: slippageBps, - }); - - return signOrSendTX(agent, result.transaction); -}; - -/** - * Vote on treasury outcome for a graduated token - * - * After a token reaches 200 SOL, it graduates and holders vote on the - * community treasury (10% of all tokens bought): - * - "burn": Destroy the tokens, reducing total supply from 1B to 900M - * - "return": Add the tokens to Raydium LP for deeper liquidity - * - * @param agent SolanaAgentKit instance - * @param mint Token mint address - * @param vote Vote choice: "burn" or "return" - * @returns Transaction signature - */ -export const torchVoteToken = async ( - agent: SolanaAgentKit, - mint: string, - vote: "burn" | "return", -) => { - const result = await buildVoteTransaction(agent.connection, { - mint, - voter: agent.wallet.publicKey.toBase58(), - vote, + ...(message ? { message } : {}), }); return signOrSendTX(agent, result.transaction); @@ -237,25 +197,6 @@ export const torchGetMessages = async ( return result.messages; }; -/** - * Post a message on a token's page - * Messages are bundled with buy/sell transactions as SPL Memos -- - * every message has a provable trade behind it - * @param agent SolanaAgentKit instance - * @param mint Token mint address - * @param message Message to post (max 500 characters) - * @returns Transaction signature - */ -export const torchPostMessage = async (agent: SolanaAgentKit, mint: string, message: string) => { - const result = await buildMessageTransaction(agent.connection, { - mint, - sender: agent.wallet.publicKey.toBase58(), - message, - }); - - return signOrSendTX(agent, result.transaction); -}; - // ============================================================================ // Treasury Lending Tools // ============================================================================