From f10425492cf42fad10a148a4898c9d3b7555274c Mon Sep 17 00:00:00 2001 From: aryan Date: Mon, 13 Jan 2025 03:18:45 +0530 Subject: [PATCH] feat: add lend/stake/bounty/token info action from agent kit --- .../plugin-solana/src/actions/getTokenInfo.ts | 153 +++++++++++++++ packages/plugin-solana/src/actions/gibwork.ts | 177 ++++++++++++++++++ packages/plugin-solana/src/actions/lend.ts | 153 +++++++++++++++ packages/plugin-solana/src/actions/stake.ts | 152 +++++++++++++++ 4 files changed, 635 insertions(+) create mode 100644 packages/plugin-solana/src/actions/getTokenInfo.ts create mode 100644 packages/plugin-solana/src/actions/gibwork.ts create mode 100644 packages/plugin-solana/src/actions/lend.ts create mode 100644 packages/plugin-solana/src/actions/stake.ts diff --git a/packages/plugin-solana/src/actions/getTokenInfo.ts b/packages/plugin-solana/src/actions/getTokenInfo.ts new file mode 100644 index 0000000000..638fe0b867 --- /dev/null +++ b/packages/plugin-solana/src/actions/getTokenInfo.ts @@ -0,0 +1,153 @@ + +import { elizaLogger, } from "@elizaos/core"; +import { + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { composeContext } from "@elizaos/core"; +import { generateObjectDeprecated } from "@elizaos/core"; +import { ACTIONS } from "solana-agent-kit"; +import { getSAK } from "../utils"; + +const GET_TOKEN_INFO_ACTION = ACTIONS.GET_TOKEN_DATA_ACTION; + +export interface GetTokenInfoContent extends Content { + tokenAddress: string; +} + +function isGetTokenInfoContent( + runtime: IAgentRuntime, + content: any +): content is GetTokenInfoContent { + elizaLogger.log("Content for transfer", content); + return ( + typeof content.tokenAddress === "string" + ); +} + +const getTokenInfoTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "tokenAddress": "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa", +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested token: +- Token contract address + +Respond with a JSON markdown block containing only the extracted values.`; + +export default { + name: GET_TOKEN_INFO_ACTION.name, + similes: GET_TOKEN_INFO_ACTION.similes, + validate: async (runtime: IAgentRuntime, message: Memory) => { + elizaLogger.log("Validating get token info from user:", message.userId); + + return false; + }, + description: GET_TOKEN_INFO_ACTION.description, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting GET_TOKEN_INFO handler..."); + const sak = await getSAK(runtime); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose get token info context + const getTokenInfoContext = composeContext({ + state, + template: getTokenInfoTemplate, + }); + + // Generate get token info content + const content = await generateObjectDeprecated({ + runtime, + context: getTokenInfoContext, + modelClass: ModelClass.LARGE, + }); + + // Validate get token info content + if (!isGetTokenInfoContent(runtime, content)) { + elizaLogger.error("Invalid content for GET_TOKEN_INFO action."); + if (callback) { + callback({ + text: "Unable to process get token info request. Invalid content provided.", + content: { error: "Invalid get token info content" }, + }); + } + return false; + } + + try { + + const tokenData = await sak.getTokenDataByAddress(content.tokenAddress) + + console.log("Token data:", tokenData); + + if (callback) { + callback({ + text: `Successfully retrieved token data for ${content.tokenAddress}`, + content: { + success: true, + tokenData: tokenData, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error during get token info:", error); + if (callback) { + callback({ + text: `Error getting token info: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Get token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa", + }, + }, + { + user: "{{user2}}", + content: { + text: "Get token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa", + action: "GET_TOKEN_INFO", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully retrieved token info for SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-solana/src/actions/gibwork.ts b/packages/plugin-solana/src/actions/gibwork.ts new file mode 100644 index 0000000000..9e8adbb893 --- /dev/null +++ b/packages/plugin-solana/src/actions/gibwork.ts @@ -0,0 +1,177 @@ +import { elizaLogger } from "@elizaos/core"; +import { + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { composeContext } from "@elizaos/core"; +import { generateObjectDeprecated } from "@elizaos/core"; +import { ACTIONS } from "solana-agent-kit"; +import { getSAK } from "../utils"; + +const GIBWORK_ACTION = ACTIONS.CREATE_GIBWORK_TASK_ACTION; + +export interface GibWorkContent extends Content { + title: string; + content: string; + requirements: string; + tags: string[]; + tokenMintAddress: string; + tokenAmount: number; +} + +function isGibWorkContent( + runtime: IAgentRuntime, + content: any +): content is GibWorkContent { + elizaLogger.log("Content for gibwork", content); + return ( + typeof content.title === "string" && + typeof content.content === "string" && + typeof content.requirements === "string" && + Array.isArray(content.tags) && + typeof content.tokenMintAddress === "string" && + typeof content.tokenAmount === "number" + ); +} + +const gibworkTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "title": "Build a Solana dApp", + "content": "Create a simple Solana dApp with React frontend", + "requirements": "Experience with Rust and React", + "tags": ["solana", "rust", "react"], + "tokenMintAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "tokenAmount": 100 +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the GibWork task: +- Title of the task +- Content/description of the task +- Requirements for the task +- Tags related to the task +- Token mint address for payment +- Token amount for payment + +Respond with a JSON markdown block containing only the extracted values.`; + +export default { + name: GIBWORK_ACTION.name, + similes: GIBWORK_ACTION.similes, + validate: async (runtime: IAgentRuntime, message: Memory) => { + elizaLogger.log("Validating gibwork task from user:", message.userId); + return false; + }, + description: GIBWORK_ACTION.description, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting CREATE_GIBWORK_TASK handler..."); + const sak = await getSAK(runtime); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose gibwork context + const gibworkContext = composeContext({ + state, + template: gibworkTemplate, + }); + + // Generate gibwork content + const content = await generateObjectDeprecated({ + runtime, + context: gibworkContext, + modelClass: ModelClass.LARGE, + }); + + // Validate gibwork content + if (!isGibWorkContent(runtime, content)) { + elizaLogger.error("Invalid content for CREATE_GIBWORK_TASK action."); + if (callback) { + callback({ + text: "Unable to process GibWork task creation. Invalid content provided.", + content: { error: "Invalid gibwork content" }, + }); + } + return false; + } + + try { + const gibworkResult = await sak.createGibworkTask( + content.title, + content.content, + content.requirements, + content.tags, + content.tokenMintAddress, + content.tokenAmount + ); + + console.log("GibWork task creation result:", gibworkResult); + + if (callback) { + callback({ + text: `Successfully created GibWork task: ${content.title}`, + content: { + success: true, + gibworkResult: gibworkResult, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error during GibWork task creation:", error); + if (callback) { + callback({ + text: `Error creating GibWork task: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Create a GibWork task for building a Solana dApp, offering 100 USDC", + }, + }, + { + user: "{{user2}}", + content: { + text: "Creating GibWork task", + action: "CREATE_GIBWORK_TASK", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully created GibWork task: Build a Solana dApp", + }, + }, + ], + ] as ActionExample[][], +} as Action; \ No newline at end of file diff --git a/packages/plugin-solana/src/actions/lend.ts b/packages/plugin-solana/src/actions/lend.ts new file mode 100644 index 0000000000..895283f541 --- /dev/null +++ b/packages/plugin-solana/src/actions/lend.ts @@ -0,0 +1,153 @@ +import { elizaLogger } from "@elizaos/core"; +import { + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { composeContext } from "@elizaos/core"; +import { generateObjectDeprecated } from "@elizaos/core"; +import { ACTIONS } from "solana-agent-kit"; +import { getSAK } from "../utils"; + +const LEND_ASSET_ACTION = ACTIONS.LEND_ASSET_ACTION; + +export interface LendAssetContent extends Content { + amount: number; +} + +function isLendAssetContent( + runtime: IAgentRuntime, + content: any +): content is LendAssetContent { + elizaLogger.log("Content for lend", content); + return ( + typeof content.amount === "number" + ); +} + +const lendTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "amount": "100", +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the lending request: +- Amount of USDC to lend + +Respond with a JSON markdown block containing only the extracted values.`; + +export default { + name: LEND_ASSET_ACTION.name, + similes: LEND_ASSET_ACTION.similes, + validate: async (runtime: IAgentRuntime, message: Memory) => { + elizaLogger.log("Validating lend asset from user:", message.userId); + return false; + }, + description: LEND_ASSET_ACTION.description, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting LEND_ASSET handler..."); + const sak = await getSAK(runtime); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose lend context + const lendContext = composeContext({ + state, + template: lendTemplate, + }); + + // Generate lend content + const content = await generateObjectDeprecated({ + runtime, + context: lendContext, + modelClass: ModelClass.LARGE, + }); + + // Validate lend content + if (!isLendAssetContent(runtime, content)) { + elizaLogger.error("Invalid content for LEND_ASSET action."); + if (callback) { + callback({ + text: "Unable to process lending request. Invalid content provided.", + content: { error: "Invalid lend content" }, + }); + } + return false; + } + + try { + const lendResult = await sak.lendAssets( + + content.amount + ); + + console.log("Lend result:", lendResult); + + if (callback) { + callback({ + text: `Successfully lent ${content.amount} USDC`, + content: { + success: true, + lendResult: lendResult, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error during lending:", error); + if (callback) { + callback({ + text: `Error lending asset: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "I want to lend 100 USDC", + }, + }, + { + user: "{{user2}}", + content: { + text: "Lend 100 USDC", + action: "LEND_ASSET", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully lent 100 USDC", + }, + }, + ], + ] as ActionExample[][], +} as Action; \ No newline at end of file diff --git a/packages/plugin-solana/src/actions/stake.ts b/packages/plugin-solana/src/actions/stake.ts new file mode 100644 index 0000000000..541d1f851f --- /dev/null +++ b/packages/plugin-solana/src/actions/stake.ts @@ -0,0 +1,152 @@ +import { elizaLogger } from "@elizaos/core"; +import { + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { composeContext } from "@elizaos/core"; +import { generateObjectDeprecated } from "@elizaos/core"; +import { ACTIONS } from "solana-agent-kit"; +import { getSAK } from "../utils"; + +const STAKE_ACTION = ACTIONS.STAKE_WITH_JUP_ACTION; + +export interface StakeContent extends Content { + amount: number; +} + +function isStakeContent( + runtime: IAgentRuntime, + content: any +): content is StakeContent { + elizaLogger.log("Content for stake", content); + return ( + typeof content.amount === "number" + ); +} + +const stakeTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "amount": "100", +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the staking request: +- Amount to stake + +Respond with a JSON markdown block containing only the extracted values.`; + +export default { + name: STAKE_ACTION.name, + similes: STAKE_ACTION.similes, + validate: async (runtime: IAgentRuntime, message: Memory) => { + elizaLogger.log("Validating stake from user:", message.userId); + return false; + }, + description: STAKE_ACTION.description, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting STAKE handler..."); + const sak = await getSAK(runtime); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose stake context + const stakeContext = composeContext({ + state, + template: stakeTemplate, + }); + + // Generate stake content + const content = await generateObjectDeprecated({ + runtime, + context: stakeContext, + modelClass: ModelClass.LARGE, + }); + + // Validate stake content + if (!isStakeContent(runtime, content)) { + elizaLogger.error("Invalid content for STAKE action."); + if (callback) { + callback({ + text: "Unable to process staking request. Invalid content provided.", + content: { error: "Invalid stake content" }, + }); + } + return false; + } + + try { + const stakeResult = await sak.stake( + content.amount + ); + + console.log("Stake result:", stakeResult); + + if (callback) { + callback({ + text: `Successfully staked ${content.amount} tokens`, + content: { + success: true, + tx: stakeResult, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error during staking:", error); + if (callback) { + callback({ + text: `Error staking: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "I want to stake 100 tokens", + }, + }, + { + user: "{{user2}}", + content: { + text: "Stake 100 tokens", + action: "STAKE_WITH_JUP", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully staked 100 tokens", + }, + }, + ], + ] as ActionExample[][], +} as Action; \ No newline at end of file