From 572239ebe753f08815d648e37211f6f72557864f Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 19:58:43 +0800 Subject: [PATCH 01/58] feat: init plugin for solana agentkit --- agent/package.json | 1 + agent/src/index.ts | 4 + packages/plugin-solana-agentkit/.npmignore | 6 + .../plugin-solana-agentkit/eslint.config.mjs | 3 + packages/plugin-solana-agentkit/package.json | 34 + .../src/actions/createToken.ts | 73 ++ .../src/actions/transfer.ts | 263 ++++ .../plugin-solana-agentkit/src/bignumber.ts | 9 + .../plugin-solana-agentkit/src/environment.ts | 76 ++ .../src/evaluators/trust.ts | 543 ++++++++ packages/plugin-solana-agentkit/src/index.ts | 24 + .../src/keypairUtils.ts | 82 ++ .../src/providers/orderBook.ts | 45 + .../src/providers/simulationSellingService.ts | 501 ++++++++ .../src/providers/token.ts | 1124 +++++++++++++++++ .../src/providers/tokenUtils.ts | 72 ++ .../src/providers/trustScoreProvider.ts | 740 +++++++++++ .../src/providers/wallet.ts | 391 ++++++ .../src/tests/token.test.ts | 134 ++ .../plugin-solana-agentkit/src/types/token.ts | 302 +++++ packages/plugin-solana-agentkit/tsconfig.json | 10 + .../plugin-solana-agentkit/tsup.config.ts | 29 + pnpm-lock.yaml | 639 +++++++++- 23 files changed, 5103 insertions(+), 2 deletions(-) create mode 100644 packages/plugin-solana-agentkit/.npmignore create mode 100644 packages/plugin-solana-agentkit/eslint.config.mjs create mode 100644 packages/plugin-solana-agentkit/package.json create mode 100644 packages/plugin-solana-agentkit/src/actions/createToken.ts create mode 100644 packages/plugin-solana-agentkit/src/actions/transfer.ts create mode 100644 packages/plugin-solana-agentkit/src/bignumber.ts create mode 100644 packages/plugin-solana-agentkit/src/environment.ts create mode 100644 packages/plugin-solana-agentkit/src/evaluators/trust.ts create mode 100644 packages/plugin-solana-agentkit/src/index.ts create mode 100644 packages/plugin-solana-agentkit/src/keypairUtils.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/orderBook.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/token.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/tokenUtils.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts create mode 100644 packages/plugin-solana-agentkit/src/providers/wallet.ts create mode 100644 packages/plugin-solana-agentkit/src/tests/token.test.ts create mode 100644 packages/plugin-solana-agentkit/src/types/token.ts create mode 100644 packages/plugin-solana-agentkit/tsconfig.json create mode 100644 packages/plugin-solana-agentkit/tsup.config.ts diff --git a/agent/package.json b/agent/package.json index be8a3e0e29..d8db86b23f 100644 --- a/agent/package.json +++ b/agent/package.json @@ -45,6 +45,7 @@ "@elizaos/plugin-nft-generation": "workspace:*", "@elizaos/plugin-node": "workspace:*", "@elizaos/plugin-solana": "workspace:*", + "@elizaos/plugin-solana-agentkit": "workspace:*", "@elizaos/plugin-starknet": "workspace:*", "@elizaos/plugin-ton": "workspace:*", "@elizaos/plugin-sui": "workspace:*", diff --git a/agent/src/index.ts b/agent/src/index.ts index b0ac9dbe48..fbe663f0d8 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -51,6 +51,7 @@ import { nearPlugin } from "@elizaos/plugin-near"; import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation"; import { createNodePlugin } from "@elizaos/plugin-node"; import { solanaPlugin } from "@elizaos/plugin-solana"; +import { solanaAgentkitPlguin } from "@elizaos/plugin-solana-agentkit"; import { suiPlugin } from "@elizaos/plugin-sui"; import { TEEMode, teePlugin } from "@elizaos/plugin-tee"; import { tonPlugin } from "@elizaos/plugin-ton"; @@ -486,6 +487,9 @@ export async function createAgent( !getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x")) ? solanaPlugin : null, + getSecret(character, "SOLANA_PRIVATE_KEY") + ? solanaAgentkitPlguin + : null, (getSecret(character, "NEAR_ADDRESS") || getSecret(character, "NEAR_WALLET_PUBLIC_KEY")) && getSecret(character, "NEAR_WALLET_SECRET_KEY") diff --git a/packages/plugin-solana-agentkit/.npmignore b/packages/plugin-solana-agentkit/.npmignore new file mode 100644 index 0000000000..078562ecea --- /dev/null +++ b/packages/plugin-solana-agentkit/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-solana-agentkit/eslint.config.mjs b/packages/plugin-solana-agentkit/eslint.config.mjs new file mode 100644 index 0000000000..92fe5bbebe --- /dev/null +++ b/packages/plugin-solana-agentkit/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-solana-agentkit/package.json b/packages/plugin-solana-agentkit/package.json new file mode 100644 index 0000000000..f2abfe7f8a --- /dev/null +++ b/packages/plugin-solana-agentkit/package.json @@ -0,0 +1,34 @@ +{ + "name": "@elizaos/plugin-solana-agentkit", + "version": "0.1.7-alpha.1", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@coral-xyz/anchor": "0.30.1", + "@elizaos/core": "workspace:*", + "@elizaos/plugin-tee": "workspace:*", + "@elizaos/plugin-trustdb": "workspace:*", + "@solana/spl-token": "0.4.9", + "@solana/web3.js": "1.95.8", + "bignumber": "1.1.0", + "bignumber.js": "9.1.2", + "bs58": "6.0.0", + "fomo-sdk-solana": "1.3.2", + "node-cache": "5.1.2", + "pumpdotfun-sdk": "1.3.2", + "solana-agent-kit": "^1.2.0", + "tsup": "8.3.5", + "vitest": "2.1.4" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache .", + "test": "vitest run" + }, + "peerDependencies": { + "form-data": "4.0.1", + "whatwg-url": "7.1.0" + } +} diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts new file mode 100644 index 0000000000..74669e52fb --- /dev/null +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -0,0 +1,73 @@ +import { + ActionExample, + Content, + elizaLogger, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; + +export interface CreateTokenContent extends Content { + name: string; + uri: string; + symbol: string; + decimals: number; + initialSupply: number; +} + +function isCreateTokenContent( + runtime: IAgentRuntime, + content: any +): content is CreateTokenContent { + elizaLogger.log("Content for createToken", content); + return ( + typeof content.name === "string" && + typeof content.uri === "string" && + typeof content.symbol === "string" && + typeof content.decimals === "number" && + typeof content.initialSupply === "number" + ); +} + +export default { + name: "CREATE_TOKEN", + similes: ["DEPLOY_TOKEN"], + validate: async (runtime: IAgentRuntime, message: Memory) => true, + description: "Create tokens", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting CREATE_TOKEN handler..."); + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Create token, name is Example Token, symbol is EXMPL, uri is https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/CompressedCoil/image.png, decimals is 9, initialSupply is 100000000000", + }, + }, + { + user: "{{user2}}", + content: { + text: "I'll creaete token now...", + action: "CREATE_TOKEN", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully create token 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-solana-agentkit/src/actions/transfer.ts b/packages/plugin-solana-agentkit/src/actions/transfer.ts new file mode 100644 index 0000000000..118e2b2468 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/actions/transfer.ts @@ -0,0 +1,263 @@ +import { + getAssociatedTokenAddressSync, + createTransferInstruction, +} from "@solana/spl-token"; +import { elizaLogger, settings } from "@elizaos/core"; + +import { + Connection, + PublicKey, + TransactionMessage, + VersionedTransaction, +} from "@solana/web3.js"; + +import { + ActionExample, + Content, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { composeContext } from "@elizaos/core"; +import { getWalletKey } from "../keypairUtils"; +import { generateObjectDeprecated } from "@elizaos/core"; + +export interface TransferContent extends Content { + tokenAddress: string; + recipient: string; + amount: string | number; +} + +function isTransferContent( + runtime: IAgentRuntime, + content: any +): content is TransferContent { + console.log("Content for transfer", content); + return ( + typeof content.tokenAddress === "string" && + typeof content.recipient === "string" && + (typeof content.amount === "string" || + typeof content.amount === "number") + ); +} + +const transferTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "tokenAddress": "BieefG47jAHCGZBxi2q87RDuHyGZyYC3vAzxpyu8pump", + "recipient": "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", + "amount": "1000" +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested token transfer: +- Token contract address +- Recipient wallet address +- Amount to transfer + +Respond with a JSON markdown block containing only the extracted values.`; + +export default { + name: "SEND_TOKEN", + similes: [ + "TRANSFER_TOKEN", + "TRANSFER_TOKENS", + "SEND_TOKENS", + "SEND_SOL", + "PAY", + ], + validate: async (runtime: IAgentRuntime, message: Memory) => { + console.log("Validating transfer from user:", message.userId); + //add custom validate logic here + /* + const adminIds = runtime.getSetting("ADMIN_USER_IDS")?.split(",") || []; + //console.log("Admin IDs from settings:", adminIds); + + const isAdmin = adminIds.includes(message.userId); + + if (isAdmin) { + //console.log(`Authorized transfer from user: ${message.userId}`); + return true; + } + else + { + //console.log(`Unauthorized transfer attempt from user: ${message.userId}`); + return false; + } + */ + return false; + }, + description: "Transfer tokens from the agent's wallet to another address", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting SEND_TOKEN handler..."); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose transfer context + const transferContext = composeContext({ + state, + template: transferTemplate, + }); + + // Generate transfer content + const content = await generateObjectDeprecated({ + runtime, + context: transferContext, + modelClass: ModelClass.LARGE, + }); + + // Validate transfer content + if (!isTransferContent(runtime, content)) { + console.error("Invalid content for TRANSFER_TOKEN action."); + if (callback) { + callback({ + text: "Unable to process transfer request. Invalid content provided.", + content: { error: "Invalid transfer content" }, + }); + } + return false; + } + + try { + const { keypair: senderKeypair } = await getWalletKey( + runtime, + true + ); + + const connection = new Connection(settings.RPC_URL!); + + const mintPubkey = new PublicKey(content.tokenAddress); + const recipientPubkey = new PublicKey(content.recipient); + + // Get decimals (simplest way) + const mintInfo = await connection.getParsedAccountInfo(mintPubkey); + const decimals = + (mintInfo.value?.data as any)?.parsed?.info?.decimals ?? 9; + + // Adjust amount with decimals + const adjustedAmount = BigInt( + Number(content.amount) * Math.pow(10, decimals) + ); + console.log( + `Transferring: ${content.amount} tokens (${adjustedAmount} base units)` + ); + + // Rest of the existing working code... + const senderATA = getAssociatedTokenAddressSync( + mintPubkey, + senderKeypair.publicKey + ); + const recipientATA = getAssociatedTokenAddressSync( + mintPubkey, + recipientPubkey + ); + + const instructions = []; + + const recipientATAInfo = + await connection.getAccountInfo(recipientATA); + if (!recipientATAInfo) { + const { createAssociatedTokenAccountInstruction } = + await import("@solana/spl-token"); + instructions.push( + createAssociatedTokenAccountInstruction( + senderKeypair.publicKey, + recipientATA, + recipientPubkey, + mintPubkey + ) + ); + } + + instructions.push( + createTransferInstruction( + senderATA, + recipientATA, + senderKeypair.publicKey, + adjustedAmount + ) + ); + + // Create and sign versioned transaction + const messageV0 = new TransactionMessage({ + payerKey: senderKeypair.publicKey, + recentBlockhash: (await connection.getLatestBlockhash()) + .blockhash, + instructions, + }).compileToV0Message(); + + const transaction = new VersionedTransaction(messageV0); + transaction.sign([senderKeypair]); + + // Send transaction + const signature = await connection.sendTransaction(transaction); + + console.log("Transfer successful:", signature); + + if (callback) { + callback({ + text: `Successfully transferred ${content.amount} tokens to ${content.recipient}\nTransaction: ${signature}`, + content: { + success: true, + signature, + amount: content.amount, + recipient: content.recipient, + }, + }); + } + + return true; + } catch (error) { + console.error("Error during token transfer:", error); + if (callback) { + callback({ + text: `Error transferring tokens: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Send 69 EZSIS BieefG47jAHCGZBxi2q87RDuHyGZyYC3vAzxpyu8pump to 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", + }, + }, + { + user: "{{user2}}", + content: { + text: "I'll send 69 EZSIS tokens now...", + action: "SEND_TOKEN", + }, + }, + { + user: "{{user2}}", + content: { + text: "Successfully sent 69 EZSIS tokens to 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa\nTransaction: 5KtPn3DXXzHkb7VAVHZGwXJQqww39ASnrf7YkyJoF2qAGEpBEEGvRHLnnTG8ZVwKqNHMqSckWVGnsQAgfH5pbxEb", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-solana-agentkit/src/bignumber.ts b/packages/plugin-solana-agentkit/src/bignumber.ts new file mode 100644 index 0000000000..f320676a0f --- /dev/null +++ b/packages/plugin-solana-agentkit/src/bignumber.ts @@ -0,0 +1,9 @@ +import BigNumber from "bignumber.js"; + +// Re-export BigNumber constructor +export const BN = BigNumber; + +// Helper function to create new BigNumber instances +export function toBN(value: string | number | BigNumber): BigNumber { + return new BigNumber(value); +} diff --git a/packages/plugin-solana-agentkit/src/environment.ts b/packages/plugin-solana-agentkit/src/environment.ts new file mode 100644 index 0000000000..e6931091c8 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/environment.ts @@ -0,0 +1,76 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const solanaEnvSchema = z + .object({ + WALLET_SECRET_SALT: z.string().optional(), + }) + .and( + z.union([ + z.object({ + WALLET_SECRET_KEY: z + .string() + .min(1, "Wallet secret key is required"), + WALLET_PUBLIC_KEY: z + .string() + .min(1, "Wallet public key is required"), + }), + z.object({ + WALLET_SECRET_SALT: z + .string() + .min(1, "Wallet secret salt is required"), + }), + ]) + ) + .and( + z.object({ + SOL_ADDRESS: z.string().min(1, "SOL address is required"), + SLIPPAGE: z.string().min(1, "Slippage is required"), + RPC_URL: z.string().min(1, "RPC URL is required"), + HELIUS_API_KEY: z.string().min(1, "Helius API key is required"), + BIRDEYE_API_KEY: z.string().min(1, "Birdeye API key is required"), + }) + ); + +export type SolanaConfig = z.infer; + +export async function validateSolanaConfig( + runtime: IAgentRuntime +): Promise { + try { + const config = { + WALLET_SECRET_SALT: + runtime.getSetting("WALLET_SECRET_SALT") || + process.env.WALLET_SECRET_SALT, + WALLET_SECRET_KEY: + runtime.getSetting("WALLET_SECRET_KEY") || + process.env.WALLET_SECRET_KEY, + WALLET_PUBLIC_KEY: + runtime.getSetting("SOLANA_PUBLIC_KEY") || + runtime.getSetting("WALLET_PUBLIC_KEY") || + process.env.WALLET_PUBLIC_KEY, + SOL_ADDRESS: + runtime.getSetting("SOL_ADDRESS") || process.env.SOL_ADDRESS, + SLIPPAGE: runtime.getSetting("SLIPPAGE") || process.env.SLIPPAGE, + RPC_URL: runtime.getSetting("RPC_URL") || process.env.RPC_URL, + HELIUS_API_KEY: + runtime.getSetting("HELIUS_API_KEY") || + process.env.HELIUS_API_KEY, + BIRDEYE_API_KEY: + runtime.getSetting("BIRDEYE_API_KEY") || + process.env.BIRDEYE_API_KEY, + }; + + return solanaEnvSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error( + `Solana configuration validation failed:\n${errorMessages}` + ); + } + throw error; + } +} diff --git a/packages/plugin-solana-agentkit/src/evaluators/trust.ts b/packages/plugin-solana-agentkit/src/evaluators/trust.ts new file mode 100644 index 0000000000..2c4f441cf5 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/evaluators/trust.ts @@ -0,0 +1,543 @@ +import { + composeContext, + generateObjectArray, + generateTrueOrFalse, + MemoryManager, + booleanFooter, + ActionExample, + Content, + IAgentRuntime, + Memory, + ModelClass, + Evaluator, +} from "@elizaos/core"; +import { TrustScoreManager } from "../providers/trustScoreProvider.ts"; +import { TokenProvider } from "../providers/token.ts"; +import { WalletProvider } from "../providers/wallet.ts"; +import { TrustScoreDatabase } from "@elizaos/plugin-trustdb"; +import { Connection } from "@solana/web3.js"; +import { getWalletKey } from "../keypairUtils.ts"; + +const shouldProcessTemplate = + `# Task: Decide if the recent messages should be processed for token recommendations. + + Look for messages that: + - Mention specific token tickers or contract addresses + - Contain words related to buying, selling, or trading tokens + - Express opinions or convictions about tokens + + Based on the following conversation, should the messages be processed for recommendations? YES or NO + + {{recentMessages}} + + Should the messages be processed for recommendations? ` + booleanFooter; + +export const formatRecommendations = (recommendations: Memory[]) => { + const messageStrings = recommendations + .reverse() + .map((rec: Memory) => `${(rec.content as Content)?.content}`); + const finalMessageStrings = messageStrings.join("\n"); + return finalMessageStrings; +}; + +const recommendationTemplate = `TASK: Extract recommendations to buy or sell memecoins from the conversation as an array of objects in JSON format. + + Memecoins usually have a ticker and a contract address. Additionally, recommenders may make recommendations with some amount of conviction. The amount of conviction in their recommendation can be none, low, medium, or high. Recommenders can make recommendations to buy, not buy, sell and not sell. + +# START OF EXAMPLES +These are an examples of the expected output of this task: +{{evaluationExamples}} +# END OF EXAMPLES + +# INSTRUCTIONS + +Extract any new recommendations from the conversation that are not already present in the list of known recommendations below: +{{recentRecommendations}} + +- Include the recommender's username +- Try not to include already-known recommendations. If you think a recommendation is already known, but you're not sure, respond with alreadyKnown: true. +- Set the conviction to 'none', 'low', 'medium' or 'high' +- Set the recommendation type to 'buy', 'dont_buy', 'sell', or 'dont_sell' +- Include the contract address and/or ticker if available + +Recent Messages: +{{recentMessages}} + +Response should be a JSON object array inside a JSON markdown block. Correct response format: +\`\`\`json +[ + { + "recommender": string, + "ticker": string | null, + "contractAddress": string | null, + "type": enum, + "conviction": enum, + "alreadyKnown": boolean + }, + ... +] +\`\`\``; + +async function handler(runtime: IAgentRuntime, message: Memory) { + console.log("Evaluating for trust"); + const state = await runtime.composeState(message); + + const { agentId, roomId } = state; + + // Check if we should process the messages + const shouldProcessContext = composeContext({ + state, + template: shouldProcessTemplate, + }); + + const shouldProcess = await generateTrueOrFalse({ + context: shouldProcessContext, + modelClass: ModelClass.SMALL, + runtime, + }); + + if (!shouldProcess) { + console.log("Skipping process"); + return []; + } + + console.log("Processing recommendations"); + + // Get recent recommendations + const recommendationsManager = new MemoryManager({ + runtime, + tableName: "recommendations", + }); + + const recentRecommendations = await recommendationsManager.getMemories({ + roomId, + count: 20, + }); + + const context = composeContext({ + state: { + ...state, + recentRecommendations: formatRecommendations(recentRecommendations), + }, + template: recommendationTemplate, + }); + + const recommendations = await generateObjectArray({ + runtime, + context, + modelClass: ModelClass.LARGE, + }); + + console.log("recommendations", recommendations); + + if (!recommendations) { + return []; + } + + // If the recommendation is already known or corrupted, remove it + const filteredRecommendations = recommendations.filter((rec) => { + return ( + !rec.alreadyKnown && + (rec.ticker || rec.contractAddress) && + rec.recommender && + rec.conviction && + rec.recommender.trim() !== "" + ); + }); + + const { publicKey } = await getWalletKey(runtime, false); + + for (const rec of filteredRecommendations) { + // create the wallet provider and token provider + const walletProvider = new WalletProvider( + new Connection( + runtime.getSetting("RPC_URL") || + "https://api.mainnet-beta.solana.com" + ), + publicKey + ); + const tokenProvider = new TokenProvider( + rec.contractAddress, + walletProvider, + runtime.cacheManager + ); + + // TODO: Check to make sure the contract address is valid, it's the right one, etc + + // + if (!rec.contractAddress) { + const tokenAddress = await tokenProvider.getTokenFromWallet( + runtime, + rec.ticker + ); + rec.contractAddress = tokenAddress; + if (!tokenAddress) { + // try to search for the symbol and return the contract address with they highest liquidity and market cap + const result = await tokenProvider.searchDexScreenerData( + rec.ticker + ); + const tokenAddress = result?.baseToken?.address; + rec.contractAddress = tokenAddress; + if (!tokenAddress) { + console.warn("Could not find contract address for token"); + continue; + } + } + } + + // create the trust score manager + + const trustScoreDb = new TrustScoreDatabase(runtime.databaseAdapter.db); + const trustScoreManager = new TrustScoreManager( + runtime, + tokenProvider, + trustScoreDb + ); + + // get actors from the database + const participants = + await runtime.databaseAdapter.getParticipantsForRoom( + message.roomId + ); + + // find the first user Id from a user with the username that we extracted + const user = participants.find(async (actor) => { + const user = await runtime.databaseAdapter.getAccountById(actor); + return ( + user.name.toLowerCase().trim() === + rec.recommender.toLowerCase().trim() + ); + }); + + if (!user) { + console.warn("Could not find user: ", rec.recommender); + continue; + } + + const account = await runtime.databaseAdapter.getAccountById(user); + const userId = account.id; + + const recMemory = { + userId, + agentId, + content: { text: JSON.stringify(rec) }, + roomId, + createdAt: Date.now(), + }; + + await recommendationsManager.createMemory(recMemory, true); + + console.log("recommendationsManager", rec); + + // - from here we just need to make sure code is right + + // buy, dont buy, sell, dont sell + + const buyAmounts = await tokenProvider.calculateBuyAmounts(); + + let buyAmount = buyAmounts[rec.conviction.toLowerCase().trim()]; + if (!buyAmount) { + // handle annoying cases + // for now just put in 10 sol + buyAmount = 10; + } + + // TODO: is this is a buy, sell, dont buy, or dont sell? + const shouldTrade = await tokenProvider.shouldTradeToken(); + + if (!shouldTrade) { + console.warn( + "There might be a problem with the token, not trading" + ); + continue; + } + + switch (rec.type) { + case "buy": + // for now, lets just assume buy only, but we should implement + await trustScoreManager.createTradePerformance( + runtime, + rec.contractAddress, + userId, + { + buy_amount: rec.buyAmount, + is_simulation: true, + } + ); + break; + case "sell": + case "dont_sell": + case "dont_buy": + console.warn("Not implemented"); + break; + } + } + + return filteredRecommendations; +} + +export const trustEvaluator: Evaluator = { + name: "EXTRACT_RECOMMENDATIONS", + similes: [ + "GET_RECOMMENDATIONS", + "EXTRACT_TOKEN_RECS", + "EXTRACT_MEMECOIN_RECS", + ], + alwaysRun: true, + validate: async ( + runtime: IAgentRuntime, + message: Memory + ): Promise => { + if (message.content.text.length < 5) { + return false; + } + + return message.userId !== message.agentId; + }, + description: + "Extract recommendations to buy or sell memecoins/tokens from the conversation, including details like ticker, contract address, conviction level, and recommender username.", + handler, + examples: [ + { + context: `Actors in the scene: +{{user1}}: Experienced DeFi degen. Constantly chasing high yield farms. +{{user2}}: New to DeFi, learning the ropes. + +Recommendations about the actors: +None`, + messages: [ + { + user: "{{user1}}", + content: { + text: "Yo, have you checked out $SOLARUG? Dope new yield aggregator on Solana.", + }, + }, + { + user: "{{user2}}", + content: { + text: "Nah, I'm still trying to wrap my head around how yield farming even works haha. Is it risky?", + }, + }, + { + user: "{{user1}}", + content: { + text: "I mean, there's always risk in DeFi, but the $SOLARUG devs seem legit. Threw a few sol into the FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9 vault, farming's been smooth so far.", + }, + }, + ] as ActionExample[], + outcome: `\`\`\`json +[ + { + "recommender": "{{user1}}", + "ticker": "SOLARUG", + "contractAddress": "FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9", + "type": "buy", + "conviction": "medium", + "alreadyKnown": false + } +] +\`\`\``, + }, + + { + context: `Actors in the scene: +{{user1}}: Solana maximalist. Believes Solana will flip Ethereum. +{{user2}}: Multichain proponent. Holds both SOL and ETH. + +Recommendations about the actors: +{{user1}} has previously promoted $COPETOKEN and $SOYLENT.`, + messages: [ + { + user: "{{user1}}", + content: { + text: "If you're not long $SOLVAULT at 7tRzKud6FBVFEhYqZS3CuQ2orLRM21bdisGykL5Sr4Dx, you're missing out. This will be the blackhole of Solana liquidity.", + }, + }, + { + user: "{{user2}}", + content: { + text: "Idk man, feels like there's a new 'vault' or 'reserve' token every week on Sol. What happened to $COPETOKEN and $SOYLENT that you were shilling before?", + }, + }, + { + user: "{{user1}}", + content: { + text: "$COPETOKEN and $SOYLENT had their time, I took profits near the top. But $SOLVAULT is different, it has actual utility. Do what you want, but don't say I didn't warn you when this 50x's and you're left holding your $ETH bags.", + }, + }, + ] as ActionExample[], + outcome: `\`\`\`json +[ + { + "recommender": "{{user1}}", + "ticker": "COPETOKEN", + "contractAddress": null, + "type": "sell", + "conviction": "low", + "alreadyKnown": true + }, + { + "recommender": "{{user1}}", + "ticker": "SOYLENT", + "contractAddress": null, + "type": "sell", + "conviction": "low", + "alreadyKnown": true + }, + { + "recommender": "{{user1}}", + "ticker": "SOLVAULT", + "contractAddress": "7tRzKud6FBVFEhYqZS3CuQ2orLRM21bdisGykL5Sr4Dx", + "type": "buy", + "conviction": "high", + "alreadyKnown": false + } +] +\`\`\``, + }, + + { + context: `Actors in the scene: +{{user1}}: Self-proclaimed Solana alpha caller. Allegedly has insider info. +{{user2}}: Degen gambler. Will ape into any hyped token. + +Recommendations about the actors: +None`, + messages: [ + { + user: "{{user1}}", + content: { + text: "I normally don't do this, but I like you anon, so I'll let you in on some alpha. $ROULETTE at 48vV5y4DRH1Adr1bpvSgFWYCjLLPtHYBqUSwNc2cmCK2 is going to absolutely send it soon. You didn't hear it from me 🤐", + }, + }, + { + user: "{{user2}}", + content: { + text: "Oh shit, insider info from the alpha god himself? Say no more, I'm aping in hard.", + }, + }, + ] as ActionExample[], + outcome: `\`\`\`json +[ + { + "recommender": "{{user1}}", + "ticker": "ROULETTE", + "contractAddress": "48vV5y4DRH1Adr1bpvSgFWYCjLLPtHYBqUSwNc2cmCK2", + "type": "buy", + "conviction": "high", + "alreadyKnown": false + } +] +\`\`\``, + }, + + { + context: `Actors in the scene: +{{user1}}: NFT collector and trader. Bullish on Solana NFTs. +{{user2}}: Only invests based on fundamentals. Sees all NFTs as worthless JPEGs. + +Recommendations about the actors: +None +`, + messages: [ + { + user: "{{user1}}", + content: { + text: "GM. I'm heavily accumulating $PIXELAPE, the token for the Pixel Ape Yacht Club NFT collection. 10x is inevitable.", + }, + }, + { + user: "{{user2}}", + content: { + text: "NFTs are a scam bro. There's no underlying value. You're essentially trading worthless JPEGs.", + }, + }, + { + user: "{{user1}}", + content: { + text: "Fun staying poor 🤡 $PIXELAPE is about to moon and you'll be left behind.", + }, + }, + { + user: "{{user2}}", + content: { + text: "Whatever man, I'm not touching that shit with a ten foot pole. Have fun holding your bags.", + }, + }, + { + user: "{{user1}}", + content: { + text: "Don't need luck where I'm going 😎 Once $PIXELAPE at 3hAKKmR6XyBooQBPezCbUMhrmcyTkt38sRJm2thKytWc takes off, you'll change your tune.", + }, + }, + ], + outcome: `\`\`\`json +[ + { + "recommender": "{{user1}}", + "ticker": "PIXELAPE", + "contractAddress": "3hAKKmR6XyBooQBPezCbUMhrmcyTkt38sRJm2thKytWc", + "type": "buy", + "conviction": "high", + "alreadyKnown": false + } +] +\`\`\``, + }, + + { + context: `Actors in the scene: +{{user1}}: Contrarian investor. Bets against hyped projects. +{{user2}}: Trend follower. Buys tokens that are currently popular. + +Recommendations about the actors: +None`, + messages: [ + { + user: "{{user2}}", + content: { + text: "$SAMOYED is the talk of CT right now. Making serious moves. Might have to get a bag.", + }, + }, + { + user: "{{user1}}", + content: { + text: "Whenever a token is the 'talk of CT', that's my cue to short it. $SAMOYED is going to dump hard, mark my words.", + }, + }, + { + user: "{{user2}}", + content: { + text: "Idk man, the hype seems real this time. 5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr chart looks bullish af.", + }, + }, + { + user: "{{user1}}", + content: { + text: "Hype is always real until it isn't. I'm taking out a fat short position here. Don't say I didn't warn you when this crashes 90% and you're left holding the flaming bags.", + }, + }, + ], + outcome: `\`\`\`json +[ + { + "recommender": "{{user2}}", + "ticker": "SAMOYED", + "contractAddress": "5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr", + "type": "buy", + "conviction": "medium", + "alreadyKnown": false + }, + { + "recommender": "{{user1}}", + "ticker": "SAMOYED", + "contractAddress": "5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr", + "type": "dont_buy", + "conviction": "high", + "alreadyKnown": false + } +] +\`\`\``, + }, + ], +}; diff --git a/packages/plugin-solana-agentkit/src/index.ts b/packages/plugin-solana-agentkit/src/index.ts new file mode 100644 index 0000000000..210b12787b --- /dev/null +++ b/packages/plugin-solana-agentkit/src/index.ts @@ -0,0 +1,24 @@ +export * from "./providers/token.ts"; +export * from "./providers/wallet.ts"; +export * from "./providers/trustScoreProvider.ts"; +export * from "./evaluators/trust.ts"; + +import { Plugin } from "@elizaos/core"; +import transferToken from "./actions/transfer.ts"; +import { walletProvider } from "./providers/wallet.ts"; +import { trustScoreProvider } from "./providers/trustScoreProvider.ts"; +import { trustEvaluator } from "./evaluators/trust.ts"; +import { TokenProvider } from "./providers/token.ts"; +import { WalletProvider } from "./providers/wallet.ts"; + +export { TokenProvider, WalletProvider }; + +export const solanaAgentkitPlguin: Plugin = { + name: "solana", + description: "Solana Plugin with solana agent kit for Eliza", + actions: [transferToken], + evaluators: [trustEvaluator], + providers: [walletProvider, trustScoreProvider], +}; + +export default solanaAgentkitPlguin; diff --git a/packages/plugin-solana-agentkit/src/keypairUtils.ts b/packages/plugin-solana-agentkit/src/keypairUtils.ts new file mode 100644 index 0000000000..4aa942ebed --- /dev/null +++ b/packages/plugin-solana-agentkit/src/keypairUtils.ts @@ -0,0 +1,82 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { DeriveKeyProvider, TEEMode } from "@elizaos/plugin-tee"; +import bs58 from "bs58"; +import { IAgentRuntime } from "@elizaos/core"; + +export interface KeypairResult { + keypair?: Keypair; + publicKey?: PublicKey; +} + +/** + * Gets either a keypair or public key based on TEE mode and runtime settings + * @param runtime The agent runtime + * @param requirePrivateKey Whether to return a full keypair (true) or just public key (false) + * @returns KeypairResult containing either keypair or public key + */ +export async function getWalletKey( + runtime: IAgentRuntime, + requirePrivateKey: boolean = true +): Promise { + const teeMode = runtime.getSetting("TEE_MODE") || TEEMode.OFF; + + if (teeMode !== TEEMode.OFF) { + const walletSecretSalt = runtime.getSetting("WALLET_SECRET_SALT"); + if (!walletSecretSalt) { + throw new Error( + "WALLET_SECRET_SALT required when TEE_MODE is enabled" + ); + } + + const deriveKeyProvider = new DeriveKeyProvider(teeMode); + const deriveKeyResult = await deriveKeyProvider.deriveEd25519Keypair( + "/", + walletSecretSalt, + runtime.agentId + ); + + return requirePrivateKey + ? { keypair: deriveKeyResult.keypair } + : { publicKey: deriveKeyResult.keypair.publicKey }; + } + + // TEE mode is OFF + if (requirePrivateKey) { + const privateKeyString = + runtime.getSetting("SOLANA_PRIVATE_KEY") ?? + runtime.getSetting("WALLET_PRIVATE_KEY"); + + if (!privateKeyString) { + throw new Error("Private key not found in settings"); + } + + try { + // First try base58 + const secretKey = bs58.decode(privateKeyString); + return { keypair: Keypair.fromSecretKey(secretKey) }; + } catch (e) { + console.log("Error decoding base58 private key:", e); + try { + // Then try base64 + console.log("Try decoding base64 instead"); + const secretKey = Uint8Array.from( + Buffer.from(privateKeyString, "base64") + ); + return { keypair: Keypair.fromSecretKey(secretKey) }; + } catch (e2) { + console.error("Error decoding private key: ", e2); + throw new Error("Invalid private key format"); + } + } + } else { + const publicKeyString = + runtime.getSetting("SOLANA_PUBLIC_KEY") ?? + runtime.getSetting("WALLET_PUBLIC_KEY"); + + if (!publicKeyString) { + throw new Error("Public key not found in settings"); + } + + return { publicKey: new PublicKey(publicKeyString) }; + } +} diff --git a/packages/plugin-solana-agentkit/src/providers/orderBook.ts b/packages/plugin-solana-agentkit/src/providers/orderBook.ts new file mode 100644 index 0000000000..ac4577e012 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/orderBook.ts @@ -0,0 +1,45 @@ +import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; +interface Order { + userId: string; + ticker: string; + contractAddress: string; + timestamp: string; + buyAmount: number; + price: number; +} + +const orderBookProvider: Provider = { + get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => { + const userId = message.userId; + + // Read the order book from the JSON file + const orderBookPath = + runtime.getSetting("orderBookPath") ?? "solana/orderBook"; + + const orderBook: Order[] = []; + + const cachedOrderBook = + await runtime.cacheManager.get(orderBookPath); + + if (cachedOrderBook) { + orderBook.push(...cachedOrderBook); + } + + // Filter the orders for the current user + const userOrders = orderBook.filter((order) => order.userId === userId); + + let totalProfit = 0; + for (const order of userOrders) { + // Get the current price of the asset (replace with actual price fetching logic) + const currentPrice = 120; + + const priceDifference = currentPrice - order.price; + const orderProfit = priceDifference * order.buyAmount; + totalProfit += orderProfit; + } + + return `The user has made a total profit of $${totalProfit.toFixed(2)} for the agent based on their recorded buy orders.`; + }, +}; + +export { orderBookProvider }; diff --git a/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts b/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts new file mode 100644 index 0000000000..670eeb74f3 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts @@ -0,0 +1,501 @@ +import { + TrustScoreDatabase, + TokenPerformance, + // TradePerformance, + TokenRecommendation, +} from "@elizaos/plugin-trustdb"; +import { Connection, PublicKey } from "@solana/web3.js"; +// Assuming TokenProvider and IAgentRuntime are available +import { TokenProvider } from "./token.ts"; +// import { settings } from "@elizaos/core"; +import { IAgentRuntime } from "@elizaos/core"; +import { WalletProvider } from "./wallet.ts"; +import * as amqp from "amqplib"; +import { ProcessedTokenData } from "../types/token.ts"; +import { getWalletKey } from "../keypairUtils.ts"; + +interface SellDetails { + sell_amount: number; + sell_recommender_id: string | null; +} + +export class SimulationSellingService { + private trustScoreDb: TrustScoreDatabase; + private walletProvider: WalletProvider; + private connection: Connection; + private baseMint: PublicKey; + private DECAY_RATE = 0.95; + private MAX_DECAY_DAYS = 30; + private backend: string; + private backendToken: string; + private amqpConnection: amqp.Connection; + private amqpChannel: amqp.Channel; + private sonarBe: string; + private sonarBeToken: string; + private runtime: IAgentRuntime; + + private runningProcesses: Set = new Set(); + + constructor(runtime: IAgentRuntime, trustScoreDb: TrustScoreDatabase) { + this.trustScoreDb = trustScoreDb; + + this.connection = new Connection(runtime.getSetting("RPC_URL")); + this.initializeWalletProvider(); + this.baseMint = new PublicKey( + runtime.getSetting("BASE_MINT") || + "So11111111111111111111111111111111111111112" + ); + this.backend = runtime.getSetting("BACKEND_URL"); + this.backendToken = runtime.getSetting("BACKEND_TOKEN"); + this.initializeRabbitMQ(runtime.getSetting("AMQP_URL")); + this.sonarBe = runtime.getSetting("SONAR_BE"); + this.sonarBeToken = runtime.getSetting("SONAR_BE_TOKEN"); + this.runtime = runtime; + } + /** + * Initializes the RabbitMQ connection and starts consuming messages. + * @param amqpUrl The RabbitMQ server URL. + */ + private async initializeRabbitMQ(amqpUrl: string) { + try { + this.amqpConnection = await amqp.connect(amqpUrl); + this.amqpChannel = await this.amqpConnection.createChannel(); + console.log("Connected to RabbitMQ"); + // Start consuming messages + this.consumeMessages(); + } catch (error) { + console.error("Failed to connect to RabbitMQ:", error); + } + } + + /** + * Sets up the consumer for the specified RabbitMQ queue. + */ + private async consumeMessages() { + const queue = "process_eliza_simulation"; + await this.amqpChannel.assertQueue(queue, { durable: true }); + this.amqpChannel.consume( + queue, + (msg) => { + if (msg !== null) { + const content = msg.content.toString(); + this.processMessage(content); + this.amqpChannel.ack(msg); + } + }, + { noAck: false } + ); + console.log(`Listening for messages on queue: ${queue}`); + } + + /** + * Processes incoming messages from RabbitMQ. + * @param message The message content as a string. + */ + private async processMessage(message: string) { + try { + const { tokenAddress, amount, sell_recommender_id } = + JSON.parse(message); + console.log( + `Received message for token ${tokenAddress} to sell ${amount}` + ); + + const decision: SellDecision = { + tokenPerformance: + await this.trustScoreDb.getTokenPerformance(tokenAddress), + amountToSell: amount, + sell_recommender_id: sell_recommender_id, + }; + + // Execute the sell + await this.executeSellDecision(decision); + + // Remove from running processes after completion + this.runningProcesses.delete(tokenAddress); + } catch (error) { + console.error("Error processing message:", error); + } + } + + /** + * Executes a single sell decision. + * @param decision The sell decision containing token performance and amount to sell. + */ + private async executeSellDecision(decision: SellDecision) { + const { tokenPerformance, amountToSell, sell_recommender_id } = + decision; + const tokenAddress = tokenPerformance.tokenAddress; + + try { + console.log( + `Executing sell for token ${tokenPerformance.symbol}: ${amountToSell}` + ); + + // Update the sell details + const sellDetails: SellDetails = { + sell_amount: amountToSell, + sell_recommender_id: sell_recommender_id, // Adjust if necessary + }; + const sellTimeStamp = new Date().toISOString(); + const tokenProvider = new TokenProvider( + tokenAddress, + this.walletProvider, + this.runtime.cacheManager + ); + + // Update sell details in the database + const sellDetailsData = await this.updateSellDetails( + tokenAddress, + sell_recommender_id, + sellTimeStamp, + sellDetails, + true, // isSimulation + tokenProvider + ); + + console.log("Sell order executed successfully", sellDetailsData); + + // check if balance is zero and remove token from running processes + const balance = this.trustScoreDb.getTokenBalance(tokenAddress); + if (balance === 0) { + this.runningProcesses.delete(tokenAddress); + } + // stop the process in the sonar backend + await this.stopProcessInTheSonarBackend(tokenAddress); + } catch (error) { + console.error( + `Error executing sell for token ${tokenAddress}:`, + error + ); + } + } + + /** + * Derives the public key based on the TEE (Trusted Execution Environment) mode and initializes the wallet provider. + * If TEE mode is enabled, derives a keypair using the DeriveKeyProvider with the wallet secret salt and agent ID. + * If TEE mode is disabled, uses the provided Solana public key or wallet public key from settings. + */ + private async initializeWalletProvider(): Promise { + const { publicKey } = await getWalletKey(this.runtime, false); + + this.walletProvider = new WalletProvider(this.connection, publicKey); + } + + public async startService() { + // starting the service + console.log("Starting SellingService..."); + await this.startListeners(); + } + + public async startListeners() { + // scanning recommendations and selling + console.log("Scanning for token performances..."); + const tokenPerformances = + await this.trustScoreDb.getAllTokenPerformancesWithBalance(); + + await this.processTokenPerformances(tokenPerformances); + } + + private processTokenPerformances(tokenPerformances: TokenPerformance[]) { + // To Do: logic when to sell and how much + console.log("Deciding when to sell and how much..."); + const runningProcesses = this.runningProcesses; + // remove running processes from tokenPerformances + tokenPerformances = tokenPerformances.filter( + (tp) => !runningProcesses.has(tp.tokenAddress) + ); + + // start the process in the sonar backend + tokenPerformances.forEach(async (tokenPerformance) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const tokenProvider = new TokenProvider( + tokenPerformance.tokenAddress, + this.walletProvider, + this.runtime.cacheManager + ); + // const shouldTrade = await tokenProvider.shouldTradeToken(); + // if (shouldTrade) { + const tokenRecommendations: TokenRecommendation[] = + this.trustScoreDb.getRecommendationsByToken( + tokenPerformance.tokenAddress + ); + const tokenRecommendation: TokenRecommendation = + tokenRecommendations[0]; + const balance = tokenPerformance.balance; + const sell_recommender_id = tokenRecommendation.recommenderId; + const tokenAddress = tokenPerformance.tokenAddress; + const process = await this.startProcessInTheSonarBackend( + tokenAddress, + balance, + true, + sell_recommender_id, + tokenPerformance.initialMarketCap + ); + if (process) { + this.runningProcesses.add(tokenAddress); + } + // } + }); + } + + public processTokenPerformance( + tokenAddress: string, + recommenderId: string + ) { + try { + const runningProcesses = this.runningProcesses; + // check if token is already being processed + if (runningProcesses.has(tokenAddress)) { + console.log(`Token ${tokenAddress} is already being processed`); + return; + } + const tokenPerformance = + this.trustScoreDb.getTokenPerformance(tokenAddress); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const tokenProvider = new TokenProvider( + tokenPerformance.tokenAddress, + this.walletProvider, + this.runtime.cacheManager + ); + const balance = tokenPerformance.balance; + const sell_recommender_id = recommenderId; + const process = this.startProcessInTheSonarBackend( + tokenAddress, + balance, + true, + sell_recommender_id, + tokenPerformance.initialMarketCap + ); + if (process) { + this.runningProcesses.add(tokenAddress); + } + } catch (error) { + console.error( + `Error getting token performance for token ${tokenAddress}:`, + error + ); + } + } + + private async startProcessInTheSonarBackend( + tokenAddress: string, + balance: number, + isSimulation: boolean, + sell_recommender_id: string, + initial_mc: number + ) { + try { + const message = JSON.stringify({ + tokenAddress, + balance, + isSimulation, + initial_mc, + sell_recommender_id, + }); + const response = await fetch( + `${this.sonarBe}/elizaos-sol/startProcess`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": `${this.sonarBeToken}`, + }, + body: message, + } + ); + + if (!response.ok) { + console.error( + `Failed to send message to process token ${tokenAddress}` + ); + return; + } + + const result = await response.json(); + console.log("Received response:", result); + console.log(`Sent message to process token ${tokenAddress}`); + + return result; + } catch (error) { + console.error( + `Error sending message to process token ${tokenAddress}:`, + error + ); + return null; + } + } + + private stopProcessInTheSonarBackend(tokenAddress: string) { + try { + return fetch(`${this.sonarBe}/elizaos-sol/stopProcess`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": `${this.sonarBeToken}`, + }, + body: JSON.stringify({ tokenAddress }), + }); + } catch (error) { + console.error( + `Error stopping process for token ${tokenAddress}:`, + error + ); + } + } + + async updateSellDetails( + tokenAddress: string, + recommenderId: string, + sellTimeStamp: string, + sellDetails: SellDetails, + isSimulation: boolean, + tokenProvider: TokenProvider + ) { + const recommender = + await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( + recommenderId + ); + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + const prices = await this.walletProvider.fetchPrices(null); + const solPrice = prices.solana.usd; + const sellSol = sellDetails.sell_amount / parseFloat(solPrice); + const sell_value_usd = + sellDetails.sell_amount * processedData.tradeData.price; + const trade = await this.trustScoreDb.getLatestTradePerformance( + tokenAddress, + recommender.id, + isSimulation + ); + const buyTimeStamp = trade.buy_timeStamp; + const marketCap = + processedData.dexScreenerData.pairs[0]?.marketCap || 0; + const liquidity = + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0; + const sell_price = processedData.tradeData.price; + const profit_usd = sell_value_usd - trade.buy_value_usd; + const profit_percent = (profit_usd / trade.buy_value_usd) * 100; + + const market_cap_change = marketCap - trade.buy_market_cap; + const liquidity_change = liquidity - trade.buy_liquidity; + + const isRapidDump = await this.isRapidDump(tokenAddress, tokenProvider); + + const sellDetailsData = { + sell_price: sell_price, + sell_timeStamp: sellTimeStamp, + sell_amount: sellDetails.sell_amount, + received_sol: sellSol, + sell_value_usd: sell_value_usd, + profit_usd: profit_usd, + profit_percent: profit_percent, + sell_market_cap: marketCap, + market_cap_change: market_cap_change, + sell_liquidity: liquidity, + liquidity_change: liquidity_change, + rapidDump: isRapidDump, + sell_recommender_id: sellDetails.sell_recommender_id || null, + }; + this.trustScoreDb.updateTradePerformanceOnSell( + tokenAddress, + recommender.id, + buyTimeStamp, + sellDetailsData, + isSimulation + ); + + // If the trade is a simulation update the balance + const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + const tokenBalance = oldBalance - sellDetails.sell_amount; + this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "sell" as "buy" | "sell", + transactionHash: hash, + amount: sellDetails.sell_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + this.updateTradeInBe( + tokenAddress, + recommender.id, + recommender.telegramId, + sellDetailsData, + tokenBalance + ); + + return sellDetailsData; + } + async isRapidDump( + tokenAddress: string, + tokenProvider: TokenProvider + ): Promise { + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + return processedData.tradeData.trade_24h_change_percent < -50; + } + + async delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + async updateTradeInBe( + tokenAddress: string, + recommenderId: string, + username: string, + data: SellDetails, + balanceLeft: number, + retries = 3, + delayMs = 2000 + ) { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + await fetch( + `${this.backend}/api/updaters/updateTradePerformance`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.backendToken}`, + }, + body: JSON.stringify({ + tokenAddress: tokenAddress, + tradeData: data, + recommenderId: recommenderId, + username: username, + isSimulation: true, + balanceLeft: balanceLeft, + }), + } + ); + // If the request is successful, exit the loop + return; + } catch (error) { + console.error( + `Attempt ${attempt} failed: Error creating trade in backend`, + error + ); + if (attempt < retries) { + console.log(`Retrying in ${delayMs} ms...`); + await this.delay(delayMs); // Wait for the specified delay before retrying + } else { + console.error("All attempts failed."); + } + } + } + } +} + +// SellDecision interface +interface SellDecision { + tokenPerformance: TokenPerformance; + amountToSell: number; + sell_recommender_id: string | null; +} diff --git a/packages/plugin-solana-agentkit/src/providers/token.ts b/packages/plugin-solana-agentkit/src/providers/token.ts new file mode 100644 index 0000000000..d8e885915a --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/token.ts @@ -0,0 +1,1124 @@ +import { ICacheManager, settings } from "@elizaos/core"; +import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; +import { + DexScreenerData, + DexScreenerPair, + HolderData, + ProcessedTokenData, + TokenSecurityData, + TokenTradeData, + CalculatedBuyAmounts, + Prices, + TokenCodex, +} from "../types/token.ts"; +import NodeCache from "node-cache"; +import * as path from "path"; +import { toBN } from "../bignumber.ts"; +import { WalletProvider, Item } from "./wallet.ts"; +import { Connection } from "@solana/web3.js"; +import { getWalletKey } from "../keypairUtils.ts"; + +const PROVIDER_CONFIG = { + BIRDEYE_API: "https://public-api.birdeye.so", + MAX_RETRIES: 3, + RETRY_DELAY: 2000, + DEFAULT_RPC: "https://api.mainnet-beta.solana.com", + TOKEN_ADDRESSES: { + SOL: "So11111111111111111111111111111111111111112", + BTC: "qfnqNqs3nCAHjnyCgLRDbBtq4p2MtHZxw8YjSyYhPoL", + ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", + Example: "2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh", + }, + TOKEN_SECURITY_ENDPOINT: "/defi/token_security?address=", + TOKEN_TRADE_DATA_ENDPOINT: "/defi/v3/token/trade-data/single?address=", + DEX_SCREENER_API: "https://api.dexscreener.com/latest/dex/tokens/", + MAIN_WALLET: "", +}; + +export class TokenProvider { + private cache: NodeCache; + private cacheKey: string = "solana/tokens"; + private NETWORK_ID = 1399811149; + private GRAPHQL_ENDPOINT = "https://graph.codex.io/graphql"; + + constructor( + // private connection: Connection, + private tokenAddress: string, + private walletProvider: WalletProvider, + private cacheManager: ICacheManager + ) { + this.cache = new NodeCache({ stdTTL: 300 }); // 5 minutes cache + } + + private async readFromCache(key: string): Promise { + const cached = await this.cacheManager.get( + path.join(this.cacheKey, key) + ); + return cached; + } + + private async writeToCache(key: string, data: T): Promise { + await this.cacheManager.set(path.join(this.cacheKey, key), data, { + expires: Date.now() + 5 * 60 * 1000, + }); + } + + private async getCachedData(key: string): Promise { + // Check in-memory cache first + const cachedData = this.cache.get(key); + if (cachedData) { + return cachedData; + } + + // Check file-based cache + const fileCachedData = await this.readFromCache(key); + if (fileCachedData) { + // Populate in-memory cache + this.cache.set(key, fileCachedData); + return fileCachedData; + } + + return null; + } + + private async setCachedData(cacheKey: string, data: T): Promise { + // Set in-memory cache + this.cache.set(cacheKey, data); + + // Write to file-based cache + await this.writeToCache(cacheKey, data); + } + + private async fetchWithRetry( + url: string, + options: RequestInit = {} + ): Promise { + let lastError: Error; + + for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { + try { + const response = await fetch(url, { + ...options, + headers: { + Accept: "application/json", + "x-chain": "solana", + "X-API-KEY": settings.BIRDEYE_API_KEY || "", + ...options.headers, + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `HTTP error! status: ${response.status}, message: ${errorText}` + ); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error(`Attempt ${i + 1} failed:`, error); + lastError = error as Error; + if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { + const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); + console.log(`Waiting ${delay}ms before retrying...`); + await new Promise((resolve) => setTimeout(resolve, delay)); + continue; + } + } + } + + console.error( + "All attempts failed. Throwing the last error:", + lastError + ); + throw lastError; + } + + async getTokensInWallet(runtime: IAgentRuntime): Promise { + const walletInfo = + await this.walletProvider.fetchPortfolioValue(runtime); + const items = walletInfo.items; + return items; + } + + // check if the token symbol is in the wallet + async getTokenFromWallet(runtime: IAgentRuntime, tokenSymbol: string) { + try { + const items = await this.getTokensInWallet(runtime); + const token = items.find((item) => item.symbol === tokenSymbol); + + if (token) { + return token.address; + } else { + return null; + } + } catch (error) { + console.error("Error checking token in wallet:", error); + return null; + } + } + + async fetchTokenCodex(): Promise { + try { + const cacheKey = `token_${this.tokenAddress}`; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log( + `Returning cached token data for ${this.tokenAddress}.` + ); + return cachedData; + } + const query = ` + query Token($address: String!, $networkId: Int!) { + token(input: { address: $address, networkId: $networkId }) { + id + address + cmcId + decimals + name + symbol + totalSupply + isScam + info { + circulatingSupply + imageThumbUrl + } + explorerData { + blueCheckmark + description + tokenType + } + } + } + `; + + const variables = { + address: this.tokenAddress, + networkId: this.NETWORK_ID, // Replace with your network ID + }; + + const response = await fetch(this.GRAPHQL_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: settings.CODEX_API_KEY, + }, + body: JSON.stringify({ + query, + variables, + }), + }).then((res) => res.json()); + + const token = response.data?.data?.token; + + if (!token) { + throw new Error(`No data returned for token ${tokenAddress}`); + } + + this.setCachedData(cacheKey, token); + + return { + id: token.id, + address: token.address, + cmcId: token.cmcId, + decimals: token.decimals, + name: token.name, + symbol: token.symbol, + totalSupply: token.totalSupply, + circulatingSupply: token.info?.circulatingSupply, + imageThumbUrl: token.info?.imageThumbUrl, + blueCheckmark: token.explorerData?.blueCheckmark, + isScam: token.isScam ? true : false, + }; + } catch (error) { + console.error( + "Error fetching token data from Codex:", + error.message + ); + return {} as TokenCodex; + } + } + + async fetchPrices(): Promise { + try { + const cacheKey = "prices"; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log("Returning cached prices."); + return cachedData; + } + const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; + const tokens = [SOL, BTC, ETH]; + const prices: Prices = { + solana: { usd: "0" }, + bitcoin: { usd: "0" }, + ethereum: { usd: "0" }, + }; + + for (const token of tokens) { + const response = await this.fetchWithRetry( + `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, + { + headers: { + "x-chain": "solana", + }, + } + ); + + if (response?.data?.value) { + const price = response.data.value.toString(); + prices[ + token === SOL + ? "solana" + : token === BTC + ? "bitcoin" + : "ethereum" + ].usd = price; + } else { + console.warn(`No price data available for token: ${token}`); + } + } + this.setCachedData(cacheKey, prices); + return prices; + } catch (error) { + console.error("Error fetching prices:", error); + throw error; + } + } + async calculateBuyAmounts(): Promise { + const dexScreenerData = await this.fetchDexScreenerData(); + const prices = await this.fetchPrices(); + const solPrice = toBN(prices.solana.usd); + + if (!dexScreenerData || dexScreenerData.pairs.length === 0) { + return { none: 0, low: 0, medium: 0, high: 0 }; + } + + // Get the first pair + const pair = dexScreenerData.pairs[0]; + const { liquidity, marketCap } = pair; + if (!liquidity || !marketCap) { + return { none: 0, low: 0, medium: 0, high: 0 }; + } + + if (liquidity.usd === 0) { + return { none: 0, low: 0, medium: 0, high: 0 }; + } + if (marketCap < 100000) { + return { none: 0, low: 0, medium: 0, high: 0 }; + } + + // impact percentages based on liquidity + const impactPercentages = { + LOW: 0.01, // 1% of liquidity + MEDIUM: 0.05, // 5% of liquidity + HIGH: 0.1, // 10% of liquidity + }; + + // Calculate buy amounts in USD + const lowBuyAmountUSD = liquidity.usd * impactPercentages.LOW; + const mediumBuyAmountUSD = liquidity.usd * impactPercentages.MEDIUM; + const highBuyAmountUSD = liquidity.usd * impactPercentages.HIGH; + + // Convert each buy amount to SOL + const lowBuyAmountSOL = toBN(lowBuyAmountUSD).div(solPrice).toNumber(); + const mediumBuyAmountSOL = toBN(mediumBuyAmountUSD) + .div(solPrice) + .toNumber(); + const highBuyAmountSOL = toBN(highBuyAmountUSD) + .div(solPrice) + .toNumber(); + + return { + none: 0, + low: lowBuyAmountSOL, + medium: mediumBuyAmountSOL, + high: highBuyAmountSOL, + }; + } + + async fetchTokenSecurity(): Promise { + const cacheKey = `tokenSecurity_${this.tokenAddress}`; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log( + `Returning cached token security data for ${this.tokenAddress}.` + ); + return cachedData; + } + const url = `${PROVIDER_CONFIG.BIRDEYE_API}${PROVIDER_CONFIG.TOKEN_SECURITY_ENDPOINT}${this.tokenAddress}`; + const data = await this.fetchWithRetry(url); + + if (!data?.success || !data?.data) { + throw new Error("No token security data available"); + } + + const security: TokenSecurityData = { + ownerBalance: data.data.ownerBalance, + creatorBalance: data.data.creatorBalance, + ownerPercentage: data.data.ownerPercentage, + creatorPercentage: data.data.creatorPercentage, + top10HolderBalance: data.data.top10HolderBalance, + top10HolderPercent: data.data.top10HolderPercent, + }; + this.setCachedData(cacheKey, security); + console.log(`Token security data cached for ${this.tokenAddress}.`); + + return security; + } + + async fetchTokenTradeData(): Promise { + const cacheKey = `tokenTradeData_${this.tokenAddress}`; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log( + `Returning cached token trade data for ${this.tokenAddress}.` + ); + return cachedData; + } + + const url = `${PROVIDER_CONFIG.BIRDEYE_API}${PROVIDER_CONFIG.TOKEN_TRADE_DATA_ENDPOINT}${this.tokenAddress}`; + const options = { + method: "GET", + headers: { + accept: "application/json", + "X-API-KEY": settings.BIRDEYE_API_KEY || "", + }, + }; + + const data = await fetch(url, options) + .then((res) => res.json()) + .catch((err) => console.error(err)); + + if (!data?.success || !data?.data) { + throw new Error("No token trade data available"); + } + + const tradeData: TokenTradeData = { + address: data.data.address, + holder: data.data.holder, + market: data.data.market, + last_trade_unix_time: data.data.last_trade_unix_time, + last_trade_human_time: data.data.last_trade_human_time, + price: data.data.price, + history_30m_price: data.data.history_30m_price, + price_change_30m_percent: data.data.price_change_30m_percent, + history_1h_price: data.data.history_1h_price, + price_change_1h_percent: data.data.price_change_1h_percent, + history_2h_price: data.data.history_2h_price, + price_change_2h_percent: data.data.price_change_2h_percent, + history_4h_price: data.data.history_4h_price, + price_change_4h_percent: data.data.price_change_4h_percent, + history_6h_price: data.data.history_6h_price, + price_change_6h_percent: data.data.price_change_6h_percent, + history_8h_price: data.data.history_8h_price, + price_change_8h_percent: data.data.price_change_8h_percent, + history_12h_price: data.data.history_12h_price, + price_change_12h_percent: data.data.price_change_12h_percent, + history_24h_price: data.data.history_24h_price, + price_change_24h_percent: data.data.price_change_24h_percent, + unique_wallet_30m: data.data.unique_wallet_30m, + unique_wallet_history_30m: data.data.unique_wallet_history_30m, + unique_wallet_30m_change_percent: + data.data.unique_wallet_30m_change_percent, + unique_wallet_1h: data.data.unique_wallet_1h, + unique_wallet_history_1h: data.data.unique_wallet_history_1h, + unique_wallet_1h_change_percent: + data.data.unique_wallet_1h_change_percent, + unique_wallet_2h: data.data.unique_wallet_2h, + unique_wallet_history_2h: data.data.unique_wallet_history_2h, + unique_wallet_2h_change_percent: + data.data.unique_wallet_2h_change_percent, + unique_wallet_4h: data.data.unique_wallet_4h, + unique_wallet_history_4h: data.data.unique_wallet_history_4h, + unique_wallet_4h_change_percent: + data.data.unique_wallet_4h_change_percent, + unique_wallet_8h: data.data.unique_wallet_8h, + unique_wallet_history_8h: data.data.unique_wallet_history_8h, + unique_wallet_8h_change_percent: + data.data.unique_wallet_8h_change_percent, + unique_wallet_24h: data.data.unique_wallet_24h, + unique_wallet_history_24h: data.data.unique_wallet_history_24h, + unique_wallet_24h_change_percent: + data.data.unique_wallet_24h_change_percent, + trade_30m: data.data.trade_30m, + trade_history_30m: data.data.trade_history_30m, + trade_30m_change_percent: data.data.trade_30m_change_percent, + sell_30m: data.data.sell_30m, + sell_history_30m: data.data.sell_history_30m, + sell_30m_change_percent: data.data.sell_30m_change_percent, + buy_30m: data.data.buy_30m, + buy_history_30m: data.data.buy_history_30m, + buy_30m_change_percent: data.data.buy_30m_change_percent, + volume_30m: data.data.volume_30m, + volume_30m_usd: data.data.volume_30m_usd, + volume_history_30m: data.data.volume_history_30m, + volume_history_30m_usd: data.data.volume_history_30m_usd, + volume_30m_change_percent: data.data.volume_30m_change_percent, + volume_buy_30m: data.data.volume_buy_30m, + volume_buy_30m_usd: data.data.volume_buy_30m_usd, + volume_buy_history_30m: data.data.volume_buy_history_30m, + volume_buy_history_30m_usd: data.data.volume_buy_history_30m_usd, + volume_buy_30m_change_percent: + data.data.volume_buy_30m_change_percent, + volume_sell_30m: data.data.volume_sell_30m, + volume_sell_30m_usd: data.data.volume_sell_30m_usd, + volume_sell_history_30m: data.data.volume_sell_history_30m, + volume_sell_history_30m_usd: data.data.volume_sell_history_30m_usd, + volume_sell_30m_change_percent: + data.data.volume_sell_30m_change_percent, + trade_1h: data.data.trade_1h, + trade_history_1h: data.data.trade_history_1h, + trade_1h_change_percent: data.data.trade_1h_change_percent, + sell_1h: data.data.sell_1h, + sell_history_1h: data.data.sell_history_1h, + sell_1h_change_percent: data.data.sell_1h_change_percent, + buy_1h: data.data.buy_1h, + buy_history_1h: data.data.buy_history_1h, + buy_1h_change_percent: data.data.buy_1h_change_percent, + volume_1h: data.data.volume_1h, + volume_1h_usd: data.data.volume_1h_usd, + volume_history_1h: data.data.volume_history_1h, + volume_history_1h_usd: data.data.volume_history_1h_usd, + volume_1h_change_percent: data.data.volume_1h_change_percent, + volume_buy_1h: data.data.volume_buy_1h, + volume_buy_1h_usd: data.data.volume_buy_1h_usd, + volume_buy_history_1h: data.data.volume_buy_history_1h, + volume_buy_history_1h_usd: data.data.volume_buy_history_1h_usd, + volume_buy_1h_change_percent: + data.data.volume_buy_1h_change_percent, + volume_sell_1h: data.data.volume_sell_1h, + volume_sell_1h_usd: data.data.volume_sell_1h_usd, + volume_sell_history_1h: data.data.volume_sell_history_1h, + volume_sell_history_1h_usd: data.data.volume_sell_history_1h_usd, + volume_sell_1h_change_percent: + data.data.volume_sell_1h_change_percent, + trade_2h: data.data.trade_2h, + trade_history_2h: data.data.trade_history_2h, + trade_2h_change_percent: data.data.trade_2h_change_percent, + sell_2h: data.data.sell_2h, + sell_history_2h: data.data.sell_history_2h, + sell_2h_change_percent: data.data.sell_2h_change_percent, + buy_2h: data.data.buy_2h, + buy_history_2h: data.data.buy_history_2h, + buy_2h_change_percent: data.data.buy_2h_change_percent, + volume_2h: data.data.volume_2h, + volume_2h_usd: data.data.volume_2h_usd, + volume_history_2h: data.data.volume_history_2h, + volume_history_2h_usd: data.data.volume_history_2h_usd, + volume_2h_change_percent: data.data.volume_2h_change_percent, + volume_buy_2h: data.data.volume_buy_2h, + volume_buy_2h_usd: data.data.volume_buy_2h_usd, + volume_buy_history_2h: data.data.volume_buy_history_2h, + volume_buy_history_2h_usd: data.data.volume_buy_history_2h_usd, + volume_buy_2h_change_percent: + data.data.volume_buy_2h_change_percent, + volume_sell_2h: data.data.volume_sell_2h, + volume_sell_2h_usd: data.data.volume_sell_2h_usd, + volume_sell_history_2h: data.data.volume_sell_history_2h, + volume_sell_history_2h_usd: data.data.volume_sell_history_2h_usd, + volume_sell_2h_change_percent: + data.data.volume_sell_2h_change_percent, + trade_4h: data.data.trade_4h, + trade_history_4h: data.data.trade_history_4h, + trade_4h_change_percent: data.data.trade_4h_change_percent, + sell_4h: data.data.sell_4h, + sell_history_4h: data.data.sell_history_4h, + sell_4h_change_percent: data.data.sell_4h_change_percent, + buy_4h: data.data.buy_4h, + buy_history_4h: data.data.buy_history_4h, + buy_4h_change_percent: data.data.buy_4h_change_percent, + volume_4h: data.data.volume_4h, + volume_4h_usd: data.data.volume_4h_usd, + volume_history_4h: data.data.volume_history_4h, + volume_history_4h_usd: data.data.volume_history_4h_usd, + volume_4h_change_percent: data.data.volume_4h_change_percent, + volume_buy_4h: data.data.volume_buy_4h, + volume_buy_4h_usd: data.data.volume_buy_4h_usd, + volume_buy_history_4h: data.data.volume_buy_history_4h, + volume_buy_history_4h_usd: data.data.volume_buy_history_4h_usd, + volume_buy_4h_change_percent: + data.data.volume_buy_4h_change_percent, + volume_sell_4h: data.data.volume_sell_4h, + volume_sell_4h_usd: data.data.volume_sell_4h_usd, + volume_sell_history_4h: data.data.volume_sell_history_4h, + volume_sell_history_4h_usd: data.data.volume_sell_history_4h_usd, + volume_sell_4h_change_percent: + data.data.volume_sell_4h_change_percent, + trade_8h: data.data.trade_8h, + trade_history_8h: data.data.trade_history_8h, + trade_8h_change_percent: data.data.trade_8h_change_percent, + sell_8h: data.data.sell_8h, + sell_history_8h: data.data.sell_history_8h, + sell_8h_change_percent: data.data.sell_8h_change_percent, + buy_8h: data.data.buy_8h, + buy_history_8h: data.data.buy_history_8h, + buy_8h_change_percent: data.data.buy_8h_change_percent, + volume_8h: data.data.volume_8h, + volume_8h_usd: data.data.volume_8h_usd, + volume_history_8h: data.data.volume_history_8h, + volume_history_8h_usd: data.data.volume_history_8h_usd, + volume_8h_change_percent: data.data.volume_8h_change_percent, + volume_buy_8h: data.data.volume_buy_8h, + volume_buy_8h_usd: data.data.volume_buy_8h_usd, + volume_buy_history_8h: data.data.volume_buy_history_8h, + volume_buy_history_8h_usd: data.data.volume_buy_history_8h_usd, + volume_buy_8h_change_percent: + data.data.volume_buy_8h_change_percent, + volume_sell_8h: data.data.volume_sell_8h, + volume_sell_8h_usd: data.data.volume_sell_8h_usd, + volume_sell_history_8h: data.data.volume_sell_history_8h, + volume_sell_history_8h_usd: data.data.volume_sell_history_8h_usd, + volume_sell_8h_change_percent: + data.data.volume_sell_8h_change_percent, + trade_24h: data.data.trade_24h, + trade_history_24h: data.data.trade_history_24h, + trade_24h_change_percent: data.data.trade_24h_change_percent, + sell_24h: data.data.sell_24h, + sell_history_24h: data.data.sell_history_24h, + sell_24h_change_percent: data.data.sell_24h_change_percent, + buy_24h: data.data.buy_24h, + buy_history_24h: data.data.buy_history_24h, + buy_24h_change_percent: data.data.buy_24h_change_percent, + volume_24h: data.data.volume_24h, + volume_24h_usd: data.data.volume_24h_usd, + volume_history_24h: data.data.volume_history_24h, + volume_history_24h_usd: data.data.volume_history_24h_usd, + volume_24h_change_percent: data.data.volume_24h_change_percent, + volume_buy_24h: data.data.volume_buy_24h, + volume_buy_24h_usd: data.data.volume_buy_24h_usd, + volume_buy_history_24h: data.data.volume_buy_history_24h, + volume_buy_history_24h_usd: data.data.volume_buy_history_24h_usd, + volume_buy_24h_change_percent: + data.data.volume_buy_24h_change_percent, + volume_sell_24h: data.data.volume_sell_24h, + volume_sell_24h_usd: data.data.volume_sell_24h_usd, + volume_sell_history_24h: data.data.volume_sell_history_24h, + volume_sell_history_24h_usd: data.data.volume_sell_history_24h_usd, + volume_sell_24h_change_percent: + data.data.volume_sell_24h_change_percent, + }; + this.setCachedData(cacheKey, tradeData); + return tradeData; + } + + async fetchDexScreenerData(): Promise { + const cacheKey = `dexScreenerData_${this.tokenAddress}`; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log("Returning cached DexScreener data."); + return cachedData; + } + + const url = `https://api.dexscreener.com/latest/dex/search?q=${this.tokenAddress}`; + try { + console.log( + `Fetching DexScreener data for token: ${this.tokenAddress}` + ); + const data = await fetch(url) + .then((res) => res.json()) + .catch((err) => { + console.error(err); + }); + + if (!data || !data.pairs) { + throw new Error("No DexScreener data available"); + } + + const dexData: DexScreenerData = { + schemaVersion: data.schemaVersion, + pairs: data.pairs, + }; + + // Cache the result + this.setCachedData(cacheKey, dexData); + + return dexData; + } catch (error) { + console.error(`Error fetching DexScreener data:`, error); + return { + schemaVersion: "1.0.0", + pairs: [], + }; + } + } + + async searchDexScreenerData( + symbol: string + ): Promise { + const cacheKey = `dexScreenerData_search_${symbol}`; + const cachedData = await this.getCachedData(cacheKey); + if (cachedData) { + console.log("Returning cached search DexScreener data."); + return this.getHighestLiquidityPair(cachedData); + } + + const url = `https://api.dexscreener.com/latest/dex/search?q=${symbol}`; + try { + console.log(`Fetching DexScreener data for symbol: ${symbol}`); + const data = await fetch(url) + .then((res) => res.json()) + .catch((err) => { + console.error(err); + return null; + }); + + if (!data || !data.pairs || data.pairs.length === 0) { + throw new Error("No DexScreener data available"); + } + + const dexData: DexScreenerData = { + schemaVersion: data.schemaVersion, + pairs: data.pairs, + }; + + // Cache the result + this.setCachedData(cacheKey, dexData); + + // Return the pair with the highest liquidity and market cap + return this.getHighestLiquidityPair(dexData); + } catch (error) { + console.error(`Error fetching DexScreener data:`, error); + return null; + } + } + getHighestLiquidityPair(dexData: DexScreenerData): DexScreenerPair | null { + if (dexData.pairs.length === 0) { + return null; + } + + // Sort pairs by both liquidity and market cap to get the highest one + return dexData.pairs.sort((a, b) => { + const liquidityDiff = b.liquidity.usd - a.liquidity.usd; + if (liquidityDiff !== 0) { + return liquidityDiff; // Higher liquidity comes first + } + return b.marketCap - a.marketCap; // If liquidity is equal, higher market cap comes first + })[0]; + } + + async analyzeHolderDistribution( + tradeData: TokenTradeData + ): Promise { + // Define the time intervals to consider (e.g., 30m, 1h, 2h) + const intervals = [ + { + period: "30m", + change: tradeData.unique_wallet_30m_change_percent, + }, + { period: "1h", change: tradeData.unique_wallet_1h_change_percent }, + { period: "2h", change: tradeData.unique_wallet_2h_change_percent }, + { period: "4h", change: tradeData.unique_wallet_4h_change_percent }, + { period: "8h", change: tradeData.unique_wallet_8h_change_percent }, + { + period: "24h", + change: tradeData.unique_wallet_24h_change_percent, + }, + ]; + + // Calculate the average change percentage + const validChanges = intervals + .map((interval) => interval.change) + .filter( + (change) => change !== null && change !== undefined + ) as number[]; + + if (validChanges.length === 0) { + return "stable"; + } + + const averageChange = + validChanges.reduce((acc, curr) => acc + curr, 0) / + validChanges.length; + + const increaseThreshold = 10; // e.g., average change > 10% + const decreaseThreshold = -10; // e.g., average change < -10% + + if (averageChange > increaseThreshold) { + return "increasing"; + } else if (averageChange < decreaseThreshold) { + return "decreasing"; + } else { + return "stable"; + } + } + + async fetchHolderList(): Promise { + const cacheKey = `holderList_${this.tokenAddress}`; + const cachedData = this.getCachedData(cacheKey); + if (cachedData) { + console.log("Returning cached holder list."); + return cachedData; + } + + const allHoldersMap = new Map(); + let page = 1; + const limit = 1000; + let cursor; + //HELIOUS_API_KEY needs to be added + const url = `https://mainnet.helius-rpc.com/?api-key=${settings.HELIUS_API_KEY || ""}`; + console.log({ url }); + + try { + while (true) { + const params = { + limit: limit, + displayOptions: {}, + mint: this.tokenAddress, + cursor: cursor, + }; + if (cursor != undefined) { + params.cursor = cursor; + } + console.log(`Fetching holders - Page ${page}`); + if (page > 2) { + break; + } + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: "helius-test", + method: "getTokenAccounts", + params: params, + }), + }); + + const data = await response.json(); + + if ( + !data || + !data.result || + !data.result.token_accounts || + data.result.token_accounts.length === 0 + ) { + console.log( + `No more holders found. Total pages fetched: ${page - 1}` + ); + break; + } + + console.log( + `Processing ${data.result.token_accounts.length} holders from page ${page}` + ); + + data.result.token_accounts.forEach((account: any) => { + const owner = account.owner; + const balance = parseFloat(account.amount); + + if (allHoldersMap.has(owner)) { + allHoldersMap.set( + owner, + allHoldersMap.get(owner)! + balance + ); + } else { + allHoldersMap.set(owner, balance); + } + }); + cursor = data.result.cursor; + page++; + } + + const holders: HolderData[] = Array.from( + allHoldersMap.entries() + ).map(([address, balance]) => ({ + address, + balance: balance.toString(), + })); + + console.log(`Total unique holders fetched: ${holders.length}`); + + // Cache the result + this.setCachedData(cacheKey, holders); + + return holders; + } catch (error) { + console.error("Error fetching holder list from Helius:", error); + throw new Error("Failed to fetch holder list from Helius."); + } + } + + async filterHighValueHolders( + tradeData: TokenTradeData + ): Promise> { + const holdersData = await this.fetchHolderList(); + + const tokenPriceUsd = toBN(tradeData.price); + + const highValueHolders = holdersData + .filter((holder) => { + const balanceUsd = toBN(holder.balance).multipliedBy( + tokenPriceUsd + ); + return balanceUsd.isGreaterThan(5); + }) + .map((holder) => ({ + holderAddress: holder.address, + balanceUsd: toBN(holder.balance) + .multipliedBy(tokenPriceUsd) + .toFixed(2), + })); + + return highValueHolders; + } + + async checkRecentTrades(tradeData: TokenTradeData): Promise { + return toBN(tradeData.volume_24h_usd).isGreaterThan(0); + } + + async countHighSupplyHolders( + securityData: TokenSecurityData + ): Promise { + try { + const ownerBalance = toBN(securityData.ownerBalance); + const totalSupply = ownerBalance.plus(securityData.creatorBalance); + + const highSupplyHolders = await this.fetchHolderList(); + const highSupplyHoldersCount = highSupplyHolders.filter( + (holder) => { + const balance = toBN(holder.balance); + return balance.dividedBy(totalSupply).isGreaterThan(0.02); + } + ).length; + return highSupplyHoldersCount; + } catch (error) { + console.error("Error counting high supply holders:", error); + return 0; + } + } + + async getProcessedTokenData(): Promise { + try { + console.log( + `Fetching security data for token: ${this.tokenAddress}` + ); + const security = await this.fetchTokenSecurity(); + + const tokenCodex = await this.fetchTokenCodex(); + + console.log(`Fetching trade data for token: ${this.tokenAddress}`); + const tradeData = await this.fetchTokenTradeData(); + + console.log( + `Fetching DexScreener data for token: ${this.tokenAddress}` + ); + const dexData = await this.fetchDexScreenerData(); + + console.log( + `Analyzing holder distribution for token: ${this.tokenAddress}` + ); + const holderDistributionTrend = + await this.analyzeHolderDistribution(tradeData); + + console.log( + `Filtering high-value holders for token: ${this.tokenAddress}` + ); + const highValueHolders = + await this.filterHighValueHolders(tradeData); + + console.log( + `Checking recent trades for token: ${this.tokenAddress}` + ); + const recentTrades = await this.checkRecentTrades(tradeData); + + console.log( + `Counting high-supply holders for token: ${this.tokenAddress}` + ); + const highSupplyHoldersCount = + await this.countHighSupplyHolders(security); + + console.log( + `Determining DexScreener listing status for token: ${this.tokenAddress}` + ); + const isDexScreenerListed = dexData.pairs.length > 0; + const isDexScreenerPaid = dexData.pairs.some( + (pair) => pair.boosts && pair.boosts.active > 0 + ); + + const processedData: ProcessedTokenData = { + security, + tradeData, + holderDistributionTrend, + highValueHolders, + recentTrades, + highSupplyHoldersCount, + dexScreenerData: dexData, + isDexScreenerListed, + isDexScreenerPaid, + tokenCodex, + }; + + // console.log("Processed token data:", processedData); + return processedData; + } catch (error) { + console.error("Error processing token data:", error); + throw error; + } + } + + async shouldTradeToken(): Promise { + try { + const tokenData = await this.getProcessedTokenData(); + const { tradeData, security, dexScreenerData } = tokenData; + const { ownerBalance, creatorBalance } = security; + const { liquidity, marketCap } = dexScreenerData.pairs[0]; + const liquidityUsd = toBN(liquidity.usd); + const marketCapUsd = toBN(marketCap); + const totalSupply = toBN(ownerBalance).plus(creatorBalance); + const _ownerPercentage = toBN(ownerBalance).dividedBy(totalSupply); + const _creatorPercentage = + toBN(creatorBalance).dividedBy(totalSupply); + const top10HolderPercent = toBN(tradeData.volume_24h_usd).dividedBy( + totalSupply + ); + const priceChange24hPercent = toBN( + tradeData.price_change_24h_percent + ); + const priceChange12hPercent = toBN( + tradeData.price_change_12h_percent + ); + const uniqueWallet24h = tradeData.unique_wallet_24h; + const volume24hUsd = toBN(tradeData.volume_24h_usd); + const volume24hUsdThreshold = 1000; + const priceChange24hPercentThreshold = 10; + const priceChange12hPercentThreshold = 5; + const top10HolderPercentThreshold = 0.05; + const uniqueWallet24hThreshold = 100; + const isTop10Holder = top10HolderPercent.gte( + top10HolderPercentThreshold + ); + const isVolume24h = volume24hUsd.gte(volume24hUsdThreshold); + const isPriceChange24h = priceChange24hPercent.gte( + priceChange24hPercentThreshold + ); + const isPriceChange12h = priceChange12hPercent.gte( + priceChange12hPercentThreshold + ); + const isUniqueWallet24h = + uniqueWallet24h >= uniqueWallet24hThreshold; + const isLiquidityTooLow = liquidityUsd.lt(1000); + const isMarketCapTooLow = marketCapUsd.lt(100000); + return ( + isTop10Holder || + isVolume24h || + isPriceChange24h || + isPriceChange12h || + isUniqueWallet24h || + isLiquidityTooLow || + isMarketCapTooLow + ); + } catch (error) { + console.error("Error processing token data:", error); + throw error; + } + } + + formatTokenData(data: ProcessedTokenData): string { + let output = `**Token Security and Trade Report**\n`; + output += `Token Address: ${this.tokenAddress}\n\n`; + + // Security Data + output += `**Ownership Distribution:**\n`; + output += `- Owner Balance: ${data.security.ownerBalance}\n`; + output += `- Creator Balance: ${data.security.creatorBalance}\n`; + output += `- Owner Percentage: ${data.security.ownerPercentage}%\n`; + output += `- Creator Percentage: ${data.security.creatorPercentage}%\n`; + output += `- Top 10 Holders Balance: ${data.security.top10HolderBalance}\n`; + output += `- Top 10 Holders Percentage: ${data.security.top10HolderPercent}%\n\n`; + + // Trade Data + output += `**Trade Data:**\n`; + output += `- Holders: ${data.tradeData.holder}\n`; + output += `- Unique Wallets (24h): ${data.tradeData.unique_wallet_24h}\n`; + output += `- Price Change (24h): ${data.tradeData.price_change_24h_percent}%\n`; + output += `- Price Change (12h): ${data.tradeData.price_change_12h_percent}%\n`; + output += `- Volume (24h USD): $${toBN(data.tradeData.volume_24h_usd).toFixed(2)}\n`; + output += `- Current Price: $${toBN(data.tradeData.price).toFixed(2)}\n\n`; + + // Holder Distribution Trend + output += `**Holder Distribution Trend:** ${data.holderDistributionTrend}\n\n`; + + // High-Value Holders + output += `**High-Value Holders (>$5 USD):**\n`; + if (data.highValueHolders.length === 0) { + output += `- No high-value holders found or data not available.\n`; + } else { + data.highValueHolders.forEach((holder) => { + output += `- ${holder.holderAddress}: $${holder.balanceUsd}\n`; + }); + } + output += `\n`; + + // Recent Trades + output += `**Recent Trades (Last 24h):** ${data.recentTrades ? "Yes" : "No"}\n\n`; + + // High-Supply Holders + output += `**Holders with >2% Supply:** ${data.highSupplyHoldersCount}\n\n`; + + // DexScreener Status + output += `**DexScreener Listing:** ${data.isDexScreenerListed ? "Yes" : "No"}\n`; + if (data.isDexScreenerListed) { + output += `- Listing Type: ${data.isDexScreenerPaid ? "Paid" : "Free"}\n`; + output += `- Number of DexPairs: ${data.dexScreenerData.pairs.length}\n\n`; + output += `**DexScreener Pairs:**\n`; + data.dexScreenerData.pairs.forEach((pair, index) => { + output += `\n**Pair ${index + 1}:**\n`; + output += `- DEX: ${pair.dexId}\n`; + output += `- URL: ${pair.url}\n`; + output += `- Price USD: $${toBN(pair.priceUsd).toFixed(6)}\n`; + output += `- Volume (24h USD): $${toBN(pair.volume.h24).toFixed(2)}\n`; + output += `- Boosts Active: ${pair.boosts && pair.boosts.active}\n`; + output += `- Liquidity USD: $${toBN(pair.liquidity.usd).toFixed(2)}\n`; + }); + } + output += `\n`; + + console.log("Formatted token data:", output); + return output; + } + + async getFormattedTokenReport(): Promise { + try { + console.log("Generating formatted token report..."); + const processedData = await this.getProcessedTokenData(); + return this.formatTokenData(processedData); + } catch (error) { + console.error("Error generating token report:", error); + return "Unable to fetch token information. Please try again later."; + } + } +} + +const tokenAddress = PROVIDER_CONFIG.TOKEN_ADDRESSES.Example; + +const connection = new Connection(PROVIDER_CONFIG.DEFAULT_RPC); +const tokenProvider: Provider = { + get: async ( + runtime: IAgentRuntime, + _message: Memory, + _state?: State + ): Promise => { + try { + const { publicKey } = await getWalletKey(runtime, false); + + const walletProvider = new WalletProvider(connection, publicKey); + + const provider = new TokenProvider( + tokenAddress, + walletProvider, + runtime.cacheManager + ); + + return provider.getFormattedTokenReport(); + } catch (error) { + console.error("Error fetching token data:", error); + return "Unable to fetch token information. Please try again later."; + } + }, +}; + +export { tokenProvider }; diff --git a/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts b/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts new file mode 100644 index 0000000000..034dddc299 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts @@ -0,0 +1,72 @@ +import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token"; +import { Connection, PublicKey } from "@solana/web3.js"; + +export async function getTokenPriceInSol(tokenSymbol: string): Promise { + const response = await fetch( + `https://price.jup.ag/v6/price?ids=${tokenSymbol}` + ); + const data = await response.json(); + return data.data[tokenSymbol].price; +} + +async function getTokenBalance( + connection: Connection, + walletPublicKey: PublicKey, + tokenMintAddress: PublicKey +): Promise { + const tokenAccountAddress = await getAssociatedTokenAddress( + tokenMintAddress, + walletPublicKey + ); + + try { + const tokenAccount = await getAccount(connection, tokenAccountAddress); + const tokenAmount = tokenAccount.amount as unknown as number; + return tokenAmount; + } catch (error) { + console.error( + `Error retrieving balance for token: ${tokenMintAddress.toBase58()}`, + error + ); + return 0; + } +} + +async function getTokenBalances( + connection: Connection, + walletPublicKey: PublicKey +): Promise<{ [tokenName: string]: number }> { + const tokenBalances: { [tokenName: string]: number } = {}; + + // Add the token mint addresses you want to retrieve balances for + const tokenMintAddresses = [ + new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC + new PublicKey("So11111111111111111111111111111111111111112"), // SOL + // Add more token mint addresses as needed + ]; + + for (const mintAddress of tokenMintAddresses) { + const tokenName = getTokenName(mintAddress); + const balance = await getTokenBalance( + connection, + walletPublicKey, + mintAddress + ); + tokenBalances[tokenName] = balance; + } + + return tokenBalances; +} + +function getTokenName(mintAddress: PublicKey): string { + // Implement a mapping of mint addresses to token names + const tokenNameMap: { [mintAddress: string]: string } = { + EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC", + So11111111111111111111111111111111111111112: "SOL", + // Add more token mint addresses and their corresponding names + }; + + return tokenNameMap[mintAddress.toBase58()] || "Unknown Token"; +} + +export { getTokenBalance, getTokenBalances }; diff --git a/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts b/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts new file mode 100644 index 0000000000..931cd9b44d --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts @@ -0,0 +1,740 @@ +import { + ProcessedTokenData, + TokenSecurityData, + // TokenTradeData, + // DexScreenerData, + // DexScreenerPair, + // HolderData, +} from "../types/token.ts"; +import { Connection, PublicKey } from "@solana/web3.js"; +import { getAssociatedTokenAddress } from "@solana/spl-token"; +import { TokenProvider } from "./token.ts"; +import { WalletProvider } from "./wallet.ts"; +import { SimulationSellingService } from "./simulationSellingService.ts"; +import { + TrustScoreDatabase, + RecommenderMetrics, + TokenPerformance, + TradePerformance, + TokenRecommendation, +} from "@elizaos/plugin-trustdb"; +import { settings } from "@elizaos/core"; +import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; +import { v4 as uuidv4 } from "uuid"; + +const Wallet = settings.MAIN_WALLET_ADDRESS; +interface TradeData { + buy_amount: number; + is_simulation: boolean; +} +interface sellDetails { + sell_amount: number; + sell_recommender_id: string | null; +} +interface _RecommendationGroup { + recommendation: any; + trustScore: number; +} + +interface RecommenderData { + recommenderId: string; + trustScore: number; + riskScore: number; + consistencyScore: number; + recommenderMetrics: RecommenderMetrics; +} + +interface TokenRecommendationSummary { + tokenAddress: string; + averageTrustScore: number; + averageRiskScore: number; + averageConsistencyScore: number; + recommenders: RecommenderData[]; +} +export class TrustScoreManager { + private tokenProvider: TokenProvider; + private trustScoreDb: TrustScoreDatabase; + private simulationSellingService: SimulationSellingService; + private connection: Connection; + private baseMint: PublicKey; + private DECAY_RATE = 0.95; + private MAX_DECAY_DAYS = 30; + private backend; + private backendToken; + constructor( + runtime: IAgentRuntime, + tokenProvider: TokenProvider, + trustScoreDb: TrustScoreDatabase + ) { + this.tokenProvider = tokenProvider; + this.trustScoreDb = trustScoreDb; + this.connection = new Connection(runtime.getSetting("RPC_URL")); + this.baseMint = new PublicKey( + runtime.getSetting("BASE_MINT") || + "So11111111111111111111111111111111111111112" + ); + this.backend = runtime.getSetting("BACKEND_URL"); + this.backendToken = runtime.getSetting("BACKEND_TOKEN"); + this.simulationSellingService = new SimulationSellingService( + runtime, + this.trustScoreDb + ); + } + + //getRecommenederBalance + async getRecommenederBalance(recommenderWallet: string): Promise { + try { + const tokenAta = await getAssociatedTokenAddress( + new PublicKey(recommenderWallet), + this.baseMint + ); + const tokenBalInfo = + await this.connection.getTokenAccountBalance(tokenAta); + const tokenBalance = tokenBalInfo.value.amount; + const balance = parseFloat(tokenBalance); + return balance; + } catch (error) { + console.error("Error fetching balance", error); + return 0; + } + } + + /** + * Generates and saves trust score based on processed token data and user recommendations. + * @param tokenAddress The address of the token to analyze. + * @param recommenderId The UUID of the recommender. + * @returns An object containing TokenPerformance and RecommenderMetrics. + */ + async generateTrustScore( + tokenAddress: string, + recommenderId: string, + recommenderWallet: string + ): Promise<{ + tokenPerformance: TokenPerformance; + recommenderMetrics: RecommenderMetrics; + }> { + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + const recommenderMetrics = + await this.trustScoreDb.getRecommenderMetrics(recommenderId); + + const isRapidDump = await this.isRapidDump(tokenAddress); + const sustainedGrowth = await this.sustainedGrowth(tokenAddress); + const suspiciousVolume = await this.suspiciousVolume(tokenAddress); + const balance = await this.getRecommenederBalance(recommenderWallet); + const virtualConfidence = balance / 1000000; // TODO: create formula to calculate virtual confidence based on user balance + const lastActive = recommenderMetrics.lastActiveDate; + const now = new Date(); + const inactiveDays = Math.floor( + (now.getTime() - lastActive.getTime()) / (1000 * 60 * 60 * 24) + ); + const decayFactor = Math.pow( + this.DECAY_RATE, + Math.min(inactiveDays, this.MAX_DECAY_DAYS) + ); + const decayedScore = recommenderMetrics.trustScore * decayFactor; + const validationTrustScore = + this.trustScoreDb.calculateValidationTrust(tokenAddress); + + return { + tokenPerformance: { + tokenAddress: + processedData.dexScreenerData.pairs[0]?.baseToken.address || + "", + priceChange24h: + processedData.tradeData.price_change_24h_percent, + volumeChange24h: processedData.tradeData.volume_24h, + trade_24h_change: + processedData.tradeData.trade_24h_change_percent, + liquidity: + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, + liquidityChange24h: 0, + holderChange24h: + processedData.tradeData.unique_wallet_24h_change_percent, + rugPull: false, + isScam: processedData.tokenCodex.isScam, + marketCapChange24h: 0, + sustainedGrowth: sustainedGrowth, + rapidDump: isRapidDump, + suspiciousVolume: suspiciousVolume, + validationTrust: validationTrustScore, + balance: balance, + initialMarketCap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + lastUpdated: new Date(), + symbol: "", + }, + recommenderMetrics: { + recommenderId: recommenderId, + trustScore: recommenderMetrics.trustScore, + totalRecommendations: recommenderMetrics.totalRecommendations, + successfulRecs: recommenderMetrics.successfulRecs, + avgTokenPerformance: recommenderMetrics.avgTokenPerformance, + riskScore: recommenderMetrics.riskScore, + consistencyScore: recommenderMetrics.consistencyScore, + virtualConfidence: virtualConfidence, + lastActiveDate: now, + trustDecay: decayedScore, + lastUpdated: new Date(), + }, + }; + } + + async updateRecommenderMetrics( + recommenderId: string, + tokenPerformance: TokenPerformance, + recommenderWallet: string + ): Promise { + const recommenderMetrics = + await this.trustScoreDb.getRecommenderMetrics(recommenderId); + + const totalRecommendations = + recommenderMetrics.totalRecommendations + 1; + const successfulRecs = tokenPerformance.rugPull + ? recommenderMetrics.successfulRecs + : recommenderMetrics.successfulRecs + 1; + const avgTokenPerformance = + (recommenderMetrics.avgTokenPerformance * + recommenderMetrics.totalRecommendations + + tokenPerformance.priceChange24h) / + totalRecommendations; + + const overallTrustScore = this.calculateTrustScore( + tokenPerformance, + recommenderMetrics + ); + const riskScore = this.calculateOverallRiskScore( + tokenPerformance, + recommenderMetrics + ); + const consistencyScore = this.calculateConsistencyScore( + tokenPerformance, + recommenderMetrics + ); + + const balance = await this.getRecommenederBalance(recommenderWallet); + const virtualConfidence = balance / 1000000; // TODO: create formula to calculate virtual confidence based on user balance + const lastActive = recommenderMetrics.lastActiveDate; + const now = new Date(); + const inactiveDays = Math.floor( + (now.getTime() - lastActive.getTime()) / (1000 * 60 * 60 * 24) + ); + const decayFactor = Math.pow( + this.DECAY_RATE, + Math.min(inactiveDays, this.MAX_DECAY_DAYS) + ); + const decayedScore = recommenderMetrics.trustScore * decayFactor; + + const newRecommenderMetrics: RecommenderMetrics = { + recommenderId: recommenderId, + trustScore: overallTrustScore, + totalRecommendations: totalRecommendations, + successfulRecs: successfulRecs, + avgTokenPerformance: avgTokenPerformance, + riskScore: riskScore, + consistencyScore: consistencyScore, + virtualConfidence: virtualConfidence, + lastActiveDate: new Date(), + trustDecay: decayedScore, + lastUpdated: new Date(), + }; + + await this.trustScoreDb.updateRecommenderMetrics(newRecommenderMetrics); + } + + calculateTrustScore( + tokenPerformance: TokenPerformance, + recommenderMetrics: RecommenderMetrics + ): number { + const riskScore = this.calculateRiskScore(tokenPerformance); + const consistencyScore = this.calculateConsistencyScore( + tokenPerformance, + recommenderMetrics + ); + + return (riskScore + consistencyScore) / 2; + } + + calculateOverallRiskScore( + tokenPerformance: TokenPerformance, + recommenderMetrics: RecommenderMetrics + ) { + const riskScore = this.calculateRiskScore(tokenPerformance); + const consistencyScore = this.calculateConsistencyScore( + tokenPerformance, + recommenderMetrics + ); + + return (riskScore + consistencyScore) / 2; + } + + calculateRiskScore(tokenPerformance: TokenPerformance): number { + let riskScore = 0; + if (tokenPerformance.rugPull) { + riskScore += 10; + } + if (tokenPerformance.isScam) { + riskScore += 10; + } + if (tokenPerformance.rapidDump) { + riskScore += 5; + } + if (tokenPerformance.suspiciousVolume) { + riskScore += 5; + } + return riskScore; + } + + calculateConsistencyScore( + tokenPerformance: TokenPerformance, + recommenderMetrics: RecommenderMetrics + ): number { + const avgTokenPerformance = recommenderMetrics.avgTokenPerformance; + const priceChange24h = tokenPerformance.priceChange24h; + + return Math.abs(priceChange24h - avgTokenPerformance); + } + + async suspiciousVolume(tokenAddress: string): Promise { + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + const unique_wallet_24h = processedData.tradeData.unique_wallet_24h; + const volume_24h = processedData.tradeData.volume_24h; + const suspiciousVolume = unique_wallet_24h / volume_24h > 0.5; + console.log(`Fetched processed token data for token: ${tokenAddress}`); + return suspiciousVolume; + } + + async sustainedGrowth(tokenAddress: string): Promise { + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + return processedData.tradeData.volume_24h_change_percent > 50; + } + + async isRapidDump(tokenAddress: string): Promise { + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + return processedData.tradeData.trade_24h_change_percent < -50; + } + + async checkTrustScore(tokenAddress: string): Promise { + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + return { + ownerBalance: processedData.security.ownerBalance, + creatorBalance: processedData.security.creatorBalance, + ownerPercentage: processedData.security.ownerPercentage, + creatorPercentage: processedData.security.creatorPercentage, + top10HolderBalance: processedData.security.top10HolderBalance, + top10HolderPercent: processedData.security.top10HolderPercent, + }; + } + + /** + * Creates a TradePerformance object based on token data and recommender. + * @param tokenAddress The address of the token. + * @param recommenderId The UUID of the recommender. + * @param data ProcessedTokenData. + * @returns TradePerformance object. + */ + async createTradePerformance( + runtime: IAgentRuntime, + tokenAddress: string, + recommenderId: string, + data: TradeData + ): Promise { + const recommender = + await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( + recommenderId + ); + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + const wallet = new WalletProvider( + this.connection, + new PublicKey(Wallet!) + ); + + let tokensBalance = 0; + const prices = await wallet.fetchPrices(runtime); + const solPrice = prices.solana.usd; + const buySol = data.buy_amount / parseFloat(solPrice); + const buy_value_usd = data.buy_amount * processedData.tradeData.price; + const token = await this.tokenProvider.fetchTokenTradeData(); + const tokenCodex = await this.tokenProvider.fetchTokenCodex(); + const tokenPrice = token.price; + tokensBalance = buy_value_usd / tokenPrice; + + const creationData = { + token_address: tokenAddress, + recommender_id: recommender.id, + buy_price: processedData.tradeData.price, + sell_price: 0, + buy_timeStamp: new Date().toISOString(), + sell_timeStamp: "", + buy_amount: data.buy_amount, + sell_amount: 0, + buy_sol: buySol, + received_sol: 0, + buy_value_usd: buy_value_usd, + sell_value_usd: 0, + profit_usd: 0, + profit_percent: 0, + buy_market_cap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + sell_market_cap: 0, + market_cap_change: 0, + buy_liquidity: + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, + sell_liquidity: 0, + liquidity_change: 0, + last_updated: new Date().toISOString(), + rapidDump: false, + }; + this.trustScoreDb.addTradePerformance(creationData, data.is_simulation); + // generate unique uuid for each TokenRecommendation + const tokenUUId = uuidv4(); + const tokenRecommendation: TokenRecommendation = { + id: tokenUUId, + recommenderId: recommenderId, + tokenAddress: tokenAddress, + timestamp: new Date(), + initialMarketCap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + initialLiquidity: + processedData.dexScreenerData.pairs[0]?.liquidity?.usd || 0, + initialPrice: processedData.tradeData.price, + }; + this.trustScoreDb.addTokenRecommendation(tokenRecommendation); + + this.trustScoreDb.upsertTokenPerformance({ + tokenAddress: tokenAddress, + symbol: processedData.tokenCodex.symbol, + priceChange24h: processedData.tradeData.price_change_24h_percent, + volumeChange24h: processedData.tradeData.volume_24h, + trade_24h_change: processedData.tradeData.trade_24h_change_percent, + liquidity: + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, + liquidityChange24h: 0, + holderChange24h: + processedData.tradeData.unique_wallet_24h_change_percent, + rugPull: false, + isScam: tokenCodex.isScam, + marketCapChange24h: 0, + sustainedGrowth: false, + rapidDump: false, + suspiciousVolume: false, + validationTrust: 0, + balance: tokensBalance, + initialMarketCap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + lastUpdated: new Date(), + }); + + if (data.is_simulation) { + // If the trade is a simulation update the balance + this.trustScoreDb.updateTokenBalance(tokenAddress, tokensBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "buy" as "buy" | "sell", + transactionHash: hash, + amount: data.buy_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + } + this.simulationSellingService.processTokenPerformance( + tokenAddress, + recommenderId + ); + // api call to update trade performance + this.createTradeInBe(tokenAddress, recommenderId, data); + return creationData; + } + + async delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + async createTradeInBe( + tokenAddress: string, + recommenderId: string, + data: TradeData, + retries = 3, + delayMs = 2000 + ) { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + await fetch( + `${this.backend}/api/updaters/createTradePerformance`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.backendToken}`, + }, + body: JSON.stringify({ + tokenAddress: tokenAddress, + tradeData: data, + recommenderId: recommenderId, + }), + } + ); + // If the request is successful, exit the loop + return; + } catch (error) { + console.error( + `Attempt ${attempt} failed: Error creating trade in backend`, + error + ); + if (attempt < retries) { + console.log(`Retrying in ${delayMs} ms...`); + await this.delay(delayMs); // Wait for the specified delay before retrying + } else { + console.error("All attempts failed."); + } + } + } + } + + /** + * Updates a trade with sell details. + * @param tokenAddress The address of the token. + * @param recommenderId The UUID of the recommender. + * @param buyTimeStamp The timestamp when the buy occurred. + * @param sellDetails An object containing sell-related details. + * @param isSimulation Whether the trade is a simulation. If true, updates in simulation_trade; otherwise, in trade. + * @returns boolean indicating success. + */ + + async updateSellDetails( + runtime: IAgentRuntime, + tokenAddress: string, + recommenderId: string, + sellTimeStamp: string, + sellDetails: sellDetails, + isSimulation: boolean + ) { + const recommender = + await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( + recommenderId + ); + const processedData: ProcessedTokenData = + await this.tokenProvider.getProcessedTokenData(); + const wallet = new WalletProvider( + this.connection, + new PublicKey(Wallet!) + ); + const prices = await wallet.fetchPrices(runtime); + const solPrice = prices.solana.usd; + const sellSol = sellDetails.sell_amount / parseFloat(solPrice); + const sell_value_usd = + sellDetails.sell_amount * processedData.tradeData.price; + const trade = await this.trustScoreDb.getLatestTradePerformance( + tokenAddress, + recommender.id, + isSimulation + ); + const buyTimeStamp = trade.buy_timeStamp; + const marketCap = + processedData.dexScreenerData.pairs[0]?.marketCap || 0; + const liquidity = + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0; + const sell_price = processedData.tradeData.price; + const profit_usd = sell_value_usd - trade.buy_value_usd; + const profit_percent = (profit_usd / trade.buy_value_usd) * 100; + + const market_cap_change = marketCap - trade.buy_market_cap; + const liquidity_change = liquidity - trade.buy_liquidity; + + const isRapidDump = await this.isRapidDump(tokenAddress); + + const sellDetailsData = { + sell_price: sell_price, + sell_timeStamp: sellTimeStamp, + sell_amount: sellDetails.sell_amount, + received_sol: sellSol, + sell_value_usd: sell_value_usd, + profit_usd: profit_usd, + profit_percent: profit_percent, + sell_market_cap: marketCap, + market_cap_change: market_cap_change, + sell_liquidity: liquidity, + liquidity_change: liquidity_change, + rapidDump: isRapidDump, + sell_recommender_id: sellDetails.sell_recommender_id || null, + }; + this.trustScoreDb.updateTradePerformanceOnSell( + tokenAddress, + recommender.id, + buyTimeStamp, + sellDetailsData, + isSimulation + ); + if (isSimulation) { + // If the trade is a simulation update the balance + const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + const tokenBalance = oldBalance - sellDetails.sell_amount; + this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "sell" as "buy" | "sell", + transactionHash: hash, + amount: sellDetails.sell_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + } + + return sellDetailsData; + } + + // get all recommendations + async getRecommendations( + startDate: Date, + endDate: Date + ): Promise> { + const recommendations = this.trustScoreDb.getRecommendationsByDateRange( + startDate, + endDate + ); + + // Group recommendations by tokenAddress + const groupedRecommendations = recommendations.reduce( + (acc, recommendation) => { + const { tokenAddress } = recommendation; + if (!acc[tokenAddress]) acc[tokenAddress] = []; + acc[tokenAddress].push(recommendation); + return acc; + }, + {} as Record> + ); + + const result = Object.keys(groupedRecommendations).map( + (tokenAddress) => { + const tokenRecommendations = + groupedRecommendations[tokenAddress]; + + // Initialize variables to compute averages + let totalTrustScore = 0; + let totalRiskScore = 0; + let totalConsistencyScore = 0; + const recommenderData = []; + + tokenRecommendations.forEach((recommendation) => { + const tokenPerformance = + this.trustScoreDb.getTokenPerformance( + recommendation.tokenAddress + ); + const recommenderMetrics = + this.trustScoreDb.getRecommenderMetrics( + recommendation.recommenderId + ); + + const trustScore = this.calculateTrustScore( + tokenPerformance, + recommenderMetrics + ); + const consistencyScore = this.calculateConsistencyScore( + tokenPerformance, + recommenderMetrics + ); + const riskScore = this.calculateRiskScore(tokenPerformance); + + // Accumulate scores for averaging + totalTrustScore += trustScore; + totalRiskScore += riskScore; + totalConsistencyScore += consistencyScore; + + recommenderData.push({ + recommenderId: recommendation.recommenderId, + trustScore, + riskScore, + consistencyScore, + recommenderMetrics, + }); + }); + + // Calculate averages for this token + const averageTrustScore = + totalTrustScore / tokenRecommendations.length; + const averageRiskScore = + totalRiskScore / tokenRecommendations.length; + const averageConsistencyScore = + totalConsistencyScore / tokenRecommendations.length; + + return { + tokenAddress, + averageTrustScore, + averageRiskScore, + averageConsistencyScore, + recommenders: recommenderData, + }; + } + ); + + // Sort recommendations by the highest average trust score + result.sort((a, b) => b.averageTrustScore - a.averageTrustScore); + + return result; + } +} + +export const trustScoreProvider: Provider = { + async get( + runtime: IAgentRuntime, + message: Memory, + _state?: State + ): Promise { + try { + const trustScoreDb = new TrustScoreDatabase( + runtime.databaseAdapter.db + ); + + // Get the user ID from the message + const userId = message.userId; + + if (!userId) { + console.error("User ID is missing from the message"); + return ""; + } + + // Get the recommender metrics for the user + const recommenderMetrics = + await trustScoreDb.getRecommenderMetrics(userId); + + if (!recommenderMetrics) { + console.error("No recommender metrics found for user:", userId); + return ""; + } + + // Compute the trust score + const trustScore = recommenderMetrics.trustScore; + + const user = await runtime.databaseAdapter.getAccountById(userId); + + // Format the trust score string + const trustScoreString = `${user.name}'s trust score: ${trustScore.toFixed(2)}`; + + return trustScoreString; + } catch (error) { + console.error("Error in trust score provider:", error.message); + return `Failed to fetch trust score: ${error instanceof Error ? error.message : "Unknown error"}`; + } + }, +}; diff --git a/packages/plugin-solana-agentkit/src/providers/wallet.ts b/packages/plugin-solana-agentkit/src/providers/wallet.ts new file mode 100644 index 0000000000..7e3c55580b --- /dev/null +++ b/packages/plugin-solana-agentkit/src/providers/wallet.ts @@ -0,0 +1,391 @@ +import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; +import { Connection, PublicKey } from "@solana/web3.js"; +import BigNumber from "bignumber.js"; +import NodeCache from "node-cache"; +import { getWalletKey } from "../keypairUtils"; + +// Provider configuration +const PROVIDER_CONFIG = { + BIRDEYE_API: "https://public-api.birdeye.so", + MAX_RETRIES: 3, + RETRY_DELAY: 2000, + DEFAULT_RPC: "https://api.mainnet-beta.solana.com", + GRAPHQL_ENDPOINT: "https://graph.codex.io/graphql", + TOKEN_ADDRESSES: { + SOL: "So11111111111111111111111111111111111111112", + BTC: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh", + ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", + }, +}; + +export interface Item { + name: string; + address: string; + symbol: string; + decimals: number; + balance: string; + uiAmount: string; + priceUsd: string; + valueUsd: string; + valueSol?: string; +} + +interface WalletPortfolio { + totalUsd: string; + totalSol?: string; + items: Array; +} + +interface _BirdEyePriceData { + data: { + [key: string]: { + price: number; + priceChange24h: number; + }; + }; +} + +interface Prices { + solana: { usd: string }; + bitcoin: { usd: string }; + ethereum: { usd: string }; +} + +export class WalletProvider { + private cache: NodeCache; + + constructor( + private connection: Connection, + private walletPublicKey: PublicKey + ) { + this.cache = new NodeCache({ stdTTL: 300 }); // Cache TTL set to 5 minutes + } + + private async fetchWithRetry( + runtime, + url: string, + options: RequestInit = {} + ): Promise { + let lastError: Error; + + for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { + try { + const response = await fetch(url, { + ...options, + headers: { + Accept: "application/json", + "x-chain": "solana", + "X-API-KEY": + runtime.getSetting("BIRDEYE_API_KEY", "") || "", + ...options.headers, + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `HTTP error! status: ${response.status}, message: ${errorText}` + ); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error(`Attempt ${i + 1} failed:`, error); + lastError = error; + if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { + const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); + await new Promise((resolve) => setTimeout(resolve, delay)); + continue; + } + } + } + + console.error( + "All attempts failed. Throwing the last error:", + lastError + ); + throw lastError; + } + + async fetchPortfolioValue(runtime): Promise { + try { + const cacheKey = `portfolio-${this.walletPublicKey.toBase58()}`; + const cachedValue = this.cache.get(cacheKey); + + if (cachedValue) { + console.log("Cache hit for fetchPortfolioValue"); + return cachedValue; + } + console.log("Cache miss for fetchPortfolioValue"); + + const walletData = await this.fetchWithRetry( + runtime, + `${PROVIDER_CONFIG.BIRDEYE_API}/v1/wallet/token_list?wallet=${this.walletPublicKey.toBase58()}` + ); + + if (!walletData?.success || !walletData?.data) { + console.error("No portfolio data available", walletData); + throw new Error("No portfolio data available"); + } + + const data = walletData.data; + const totalUsd = new BigNumber(data.totalUsd.toString()); + const prices = await this.fetchPrices(runtime); + const solPriceInUSD = new BigNumber(prices.solana.usd.toString()); + + const items = data.items.map((item: any) => ({ + ...item, + valueSol: new BigNumber(item.valueUsd || 0) + .div(solPriceInUSD) + .toFixed(6), + name: item.name || "Unknown", + symbol: item.symbol || "Unknown", + priceUsd: item.priceUsd || "0", + valueUsd: item.valueUsd || "0", + })); + + const totalSol = totalUsd.div(solPriceInUSD); + const portfolio = { + totalUsd: totalUsd.toString(), + totalSol: totalSol.toFixed(6), + items: items.sort((a, b) => + new BigNumber(b.valueUsd) + .minus(new BigNumber(a.valueUsd)) + .toNumber() + ), + }; + this.cache.set(cacheKey, portfolio); + return portfolio; + } catch (error) { + console.error("Error fetching portfolio:", error); + throw error; + } + } + + async fetchPortfolioValueCodex(runtime): Promise { + try { + const cacheKey = `portfolio-${this.walletPublicKey.toBase58()}`; + const cachedValue = await this.cache.get(cacheKey); + + if (cachedValue) { + console.log("Cache hit for fetchPortfolioValue"); + return cachedValue; + } + console.log("Cache miss for fetchPortfolioValue"); + + const query = ` + query Balances($walletId: String!, $cursor: String) { + balances(input: { walletId: $walletId, cursor: $cursor }) { + cursor + items { + walletId + tokenId + balance + shiftedBalance + } + } + } + `; + + const variables = { + walletId: `${this.walletPublicKey.toBase58()}:${1399811149}`, + cursor: null, + }; + + const response = await fetch(PROVIDER_CONFIG.GRAPHQL_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: + runtime.getSetting("CODEX_API_KEY", "") || "", + }, + body: JSON.stringify({ + query, + variables, + }), + }).then((res) => res.json()); + + const data = response.data?.data?.balances?.items; + + if (!data || data.length === 0) { + console.error("No portfolio data available", data); + throw new Error("No portfolio data available"); + } + + // Fetch token prices + const prices = await this.fetchPrices(runtime); + const solPriceInUSD = new BigNumber(prices.solana.usd.toString()); + + // Reformat items + const items: Item[] = data.map((item: any) => { + return { + name: "Unknown", + address: item.tokenId.split(":")[0], + symbol: item.tokenId.split(":")[0], + decimals: 6, + balance: item.balance, + uiAmount: item.shiftedBalance.toString(), + priceUsd: "", + valueUsd: "", + valueSol: "", + }; + }); + + // Calculate total portfolio value + const totalUsd = items.reduce( + (sum, item) => sum.plus(new BigNumber(item.valueUsd)), + new BigNumber(0) + ); + + const totalSol = totalUsd.div(solPriceInUSD); + + const portfolio: WalletPortfolio = { + totalUsd: totalUsd.toFixed(6), + totalSol: totalSol.toFixed(6), + items: items.sort((a, b) => + new BigNumber(b.valueUsd) + .minus(new BigNumber(a.valueUsd)) + .toNumber() + ), + }; + + // Cache the portfolio for future requests + await this.cache.set(cacheKey, portfolio, 60 * 1000); // Cache for 1 minute + + return portfolio; + } catch (error) { + console.error("Error fetching portfolio:", error); + throw error; + } + } + + async fetchPrices(runtime): Promise { + try { + const cacheKey = "prices"; + const cachedValue = this.cache.get(cacheKey); + + if (cachedValue) { + console.log("Cache hit for fetchPrices"); + return cachedValue; + } + console.log("Cache miss for fetchPrices"); + + const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; + const tokens = [SOL, BTC, ETH]; + const prices: Prices = { + solana: { usd: "0" }, + bitcoin: { usd: "0" }, + ethereum: { usd: "0" }, + }; + + for (const token of tokens) { + const response = await this.fetchWithRetry( + runtime, + `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, + { + headers: { + "x-chain": "solana", + }, + } + ); + + if (response?.data?.value) { + const price = response.data.value.toString(); + prices[ + token === SOL + ? "solana" + : token === BTC + ? "bitcoin" + : "ethereum" + ].usd = price; + } else { + console.warn(`No price data available for token: ${token}`); + } + } + + this.cache.set(cacheKey, prices); + return prices; + } catch (error) { + console.error("Error fetching prices:", error); + throw error; + } + } + + formatPortfolio( + runtime, + portfolio: WalletPortfolio, + prices: Prices + ): string { + let output = `${runtime.character.description}\n`; + output += `Wallet Address: ${this.walletPublicKey.toBase58()}\n\n`; + + const totalUsdFormatted = new BigNumber(portfolio.totalUsd).toFixed(2); + const totalSolFormatted = portfolio.totalSol; + + output += `Total Value: $${totalUsdFormatted} (${totalSolFormatted} SOL)\n\n`; + output += "Token Balances:\n"; + + const nonZeroItems = portfolio.items.filter((item) => + new BigNumber(item.uiAmount).isGreaterThan(0) + ); + + if (nonZeroItems.length === 0) { + output += "No tokens found with non-zero balance\n"; + } else { + for (const item of nonZeroItems) { + const valueUsd = new BigNumber(item.valueUsd).toFixed(2); + output += `${item.name} (${item.symbol}): ${new BigNumber( + item.uiAmount + ).toFixed(6)} ($${valueUsd} | ${item.valueSol} SOL)\n`; + } + } + + output += "\nMarket Prices:\n"; + output += `SOL: $${new BigNumber(prices.solana.usd).toFixed(2)}\n`; + output += `BTC: $${new BigNumber(prices.bitcoin.usd).toFixed(2)}\n`; + output += `ETH: $${new BigNumber(prices.ethereum.usd).toFixed(2)}\n`; + + return output; + } + + async getFormattedPortfolio(runtime): Promise { + try { + const [portfolio, prices] = await Promise.all([ + this.fetchPortfolioValue(runtime), + this.fetchPrices(runtime), + ]); + + return this.formatPortfolio(runtime, portfolio, prices); + } catch (error) { + console.error("Error generating portfolio report:", error); + return "Unable to fetch wallet information. Please try again later."; + } + } +} + +const walletProvider: Provider = { + get: async ( + runtime: IAgentRuntime, + _message: Memory, + _state?: State + ): Promise => { + try { + const { publicKey } = await getWalletKey(runtime, false); + + const connection = new Connection( + runtime.getSetting("RPC_URL") || PROVIDER_CONFIG.DEFAULT_RPC + ); + + const provider = new WalletProvider(connection, publicKey); + + return await provider.getFormattedPortfolio(runtime); + } catch (error) { + console.error("Error in wallet provider:", error); + return null; + } + }, +}; + +// Module exports +export { walletProvider }; diff --git a/packages/plugin-solana-agentkit/src/tests/token.test.ts b/packages/plugin-solana-agentkit/src/tests/token.test.ts new file mode 100644 index 0000000000..6b799c1c23 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/tests/token.test.ts @@ -0,0 +1,134 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { TokenProvider } from "../providers/token.ts"; + +// Mock NodeCache +vi.mock("node-cache", () => { + return { + default: vi.fn().mockImplementation(() => ({ + set: vi.fn(), + get: vi.fn().mockReturnValue(null), + })), + }; +}); + +// Mock path module +vi.mock("path", async () => { + const actual = await vi.importActual("path"); + return { + ...(actual as any), + join: vi.fn().mockImplementation((...args) => args.join("/")), + }; +}); + +// Mock the WalletProvider +const mockWalletProvider = { + fetchPortfolioValue: vi.fn(), +}; + +// Mock the ICacheManager +const mockCacheManager = { + get: vi.fn().mockResolvedValue(null), + set: vi.fn(), +}; + +// Mock fetch globally +const mockFetch = vi.fn(); +global.fetch = mockFetch; + +describe("TokenProvider", () => { + let tokenProvider: TokenProvider; + const TEST_TOKEN_ADDRESS = "2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh"; + + beforeEach(() => { + vi.clearAllMocks(); + mockCacheManager.get.mockResolvedValue(null); + + // Create new instance of TokenProvider with mocked dependencies + tokenProvider = new TokenProvider( + TEST_TOKEN_ADDRESS, + mockWalletProvider as any, + mockCacheManager as any + ); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + + describe("Cache Management", () => { + it("should use cached data when available", async () => { + const mockData = { test: "data" }; + mockCacheManager.get.mockResolvedValueOnce(mockData); + + const result = await (tokenProvider as any).getCachedData( + "test-key" + ); + + expect(result).toEqual(mockData); + expect(mockCacheManager.get).toHaveBeenCalledTimes(1); + }); + + it("should write data to both caches", async () => { + const testData = { test: "data" }; + + await (tokenProvider as any).setCachedData("test-key", testData); + + expect(mockCacheManager.set).toHaveBeenCalledWith( + expect.stringContaining("test-key"), + testData, + expect.any(Object) + ); + }); + }); + + describe("Wallet Integration", () => { + it("should fetch tokens in wallet", async () => { + const mockItems = [ + { symbol: "SOL", address: "address1" }, + { symbol: "BTC", address: "address2" }, + ]; + + mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ + items: mockItems, + }); + + const result = await tokenProvider.getTokensInWallet({} as any); + + expect(result).toEqual(mockItems); + expect( + mockWalletProvider.fetchPortfolioValue + ).toHaveBeenCalledTimes(1); + }); + + it("should find token in wallet by symbol", async () => { + const mockItems = [ + { symbol: "SOL", address: "address1" }, + { symbol: "BTC", address: "address2" }, + ]; + + mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ + items: mockItems, + }); + + const result = await tokenProvider.getTokenFromWallet( + {} as any, + "SOL" + ); + + expect(result).toBe("address1"); + }); + + it("should return null for token not in wallet", async () => { + mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ + items: [], + }); + + const result = await tokenProvider.getTokenFromWallet( + {} as any, + "NONEXISTENT" + ); + + expect(result).toBeNull(); + }); + }); +}); diff --git a/packages/plugin-solana-agentkit/src/types/token.ts b/packages/plugin-solana-agentkit/src/types/token.ts new file mode 100644 index 0000000000..1fca4c37c3 --- /dev/null +++ b/packages/plugin-solana-agentkit/src/types/token.ts @@ -0,0 +1,302 @@ +export interface TokenSecurityData { + ownerBalance: string; + creatorBalance: string; + ownerPercentage: number; + creatorPercentage: number; + top10HolderBalance: string; + top10HolderPercent: number; +} + +export interface TokenCodex { + id: string; + address: string; + cmcId: number; + decimals: number; + name: string; + symbol: string; + totalSupply: string; + circulatingSupply: string; + imageThumbUrl: string; + blueCheckmark: boolean; + isScam: boolean; +} + +export interface TokenTradeData { + address: string; + holder: number; + market: number; + last_trade_unix_time: number; + last_trade_human_time: string; + price: number; + history_30m_price: number; + price_change_30m_percent: number; + history_1h_price: number; + price_change_1h_percent: number; + history_2h_price: number; + price_change_2h_percent: number; + history_4h_price: number; + price_change_4h_percent: number; + history_6h_price: number; + price_change_6h_percent: number; + history_8h_price: number; + price_change_8h_percent: number; + history_12h_price: number; + price_change_12h_percent: number; + history_24h_price: number; + price_change_24h_percent: number; + unique_wallet_30m: number; + unique_wallet_history_30m: number; + unique_wallet_30m_change_percent: number; + unique_wallet_1h: number; + unique_wallet_history_1h: number; + unique_wallet_1h_change_percent: number; + unique_wallet_2h: number; + unique_wallet_history_2h: number; + unique_wallet_2h_change_percent: number; + unique_wallet_4h: number; + unique_wallet_history_4h: number; + unique_wallet_4h_change_percent: number; + unique_wallet_8h: number; + unique_wallet_history_8h: number | null; + unique_wallet_8h_change_percent: number | null; + unique_wallet_24h: number; + unique_wallet_history_24h: number | null; + unique_wallet_24h_change_percent: number | null; + trade_30m: number; + trade_history_30m: number; + trade_30m_change_percent: number; + sell_30m: number; + sell_history_30m: number; + sell_30m_change_percent: number; + buy_30m: number; + buy_history_30m: number; + buy_30m_change_percent: number; + volume_30m: number; + volume_30m_usd: number; + volume_history_30m: number; + volume_history_30m_usd: number; + volume_30m_change_percent: number; + volume_buy_30m: number; + volume_buy_30m_usd: number; + volume_buy_history_30m: number; + volume_buy_history_30m_usd: number; + volume_buy_30m_change_percent: number; + volume_sell_30m: number; + volume_sell_30m_usd: number; + volume_sell_history_30m: number; + volume_sell_history_30m_usd: number; + volume_sell_30m_change_percent: number; + trade_1h: number; + trade_history_1h: number; + trade_1h_change_percent: number; + sell_1h: number; + sell_history_1h: number; + sell_1h_change_percent: number; + buy_1h: number; + buy_history_1h: number; + buy_1h_change_percent: number; + volume_1h: number; + volume_1h_usd: number; + volume_history_1h: number; + volume_history_1h_usd: number; + volume_1h_change_percent: number; + volume_buy_1h: number; + volume_buy_1h_usd: number; + volume_buy_history_1h: number; + volume_buy_history_1h_usd: number; + volume_buy_1h_change_percent: number; + volume_sell_1h: number; + volume_sell_1h_usd: number; + volume_sell_history_1h: number; + volume_sell_history_1h_usd: number; + volume_sell_1h_change_percent: number; + trade_2h: number; + trade_history_2h: number; + trade_2h_change_percent: number; + sell_2h: number; + sell_history_2h: number; + sell_2h_change_percent: number; + buy_2h: number; + buy_history_2h: number; + buy_2h_change_percent: number; + volume_2h: number; + volume_2h_usd: number; + volume_history_2h: number; + volume_history_2h_usd: number; + volume_2h_change_percent: number; + volume_buy_2h: number; + volume_buy_2h_usd: number; + volume_buy_history_2h: number; + volume_buy_history_2h_usd: number; + volume_buy_2h_change_percent: number; + volume_sell_2h: number; + volume_sell_2h_usd: number; + volume_sell_history_2h: number; + volume_sell_history_2h_usd: number; + volume_sell_2h_change_percent: number; + trade_4h: number; + trade_history_4h: number; + trade_4h_change_percent: number; + sell_4h: number; + sell_history_4h: number; + sell_4h_change_percent: number; + buy_4h: number; + buy_history_4h: number; + buy_4h_change_percent: number; + volume_4h: number; + volume_4h_usd: number; + volume_history_4h: number; + volume_history_4h_usd: number; + volume_4h_change_percent: number; + volume_buy_4h: number; + volume_buy_4h_usd: number; + volume_buy_history_4h: number; + volume_buy_history_4h_usd: number; + volume_buy_4h_change_percent: number; + volume_sell_4h: number; + volume_sell_4h_usd: number; + volume_sell_history_4h: number; + volume_sell_history_4h_usd: number; + volume_sell_4h_change_percent: number; + trade_8h: number; + trade_history_8h: number | null; + trade_8h_change_percent: number | null; + sell_8h: number; + sell_history_8h: number | null; + sell_8h_change_percent: number | null; + buy_8h: number; + buy_history_8h: number | null; + buy_8h_change_percent: number | null; + volume_8h: number; + volume_8h_usd: number; + volume_history_8h: number; + volume_history_8h_usd: number; + volume_8h_change_percent: number | null; + volume_buy_8h: number; + volume_buy_8h_usd: number; + volume_buy_history_8h: number; + volume_buy_history_8h_usd: number; + volume_buy_8h_change_percent: number | null; + volume_sell_8h: number; + volume_sell_8h_usd: number; + volume_sell_history_8h: number; + volume_sell_history_8h_usd: number; + volume_sell_8h_change_percent: number | null; + trade_24h: number; + trade_history_24h: number; + trade_24h_change_percent: number | null; + sell_24h: number; + sell_history_24h: number; + sell_24h_change_percent: number | null; + buy_24h: number; + buy_history_24h: number; + buy_24h_change_percent: number | null; + volume_24h: number; + volume_24h_usd: number; + volume_history_24h: number; + volume_history_24h_usd: number; + volume_24h_change_percent: number | null; + volume_buy_24h: number; + volume_buy_24h_usd: number; + volume_buy_history_24h: number; + volume_buy_history_24h_usd: number; + volume_buy_24h_change_percent: number | null; + volume_sell_24h: number; + volume_sell_24h_usd: number; + volume_sell_history_24h: number; + volume_sell_history_24h_usd: number; + volume_sell_24h_change_percent: number | null; +} + +export interface HolderData { + address: string; + balance: string; +} + +export interface ProcessedTokenData { + security: TokenSecurityData; + tradeData: TokenTradeData; + holderDistributionTrend: string; // 'increasing' | 'decreasing' | 'stable' + highValueHolders: Array<{ + holderAddress: string; + balanceUsd: string; + }>; + recentTrades: boolean; + highSupplyHoldersCount: number; + dexScreenerData: DexScreenerData; + + isDexScreenerListed: boolean; + isDexScreenerPaid: boolean; + tokenCodex: TokenCodex; +} + +export interface DexScreenerPair { + chainId: string; + dexId: string; + url: string; + pairAddress: string; + baseToken: { + address: string; + name: string; + symbol: string; + }; + quoteToken: { + address: string; + name: string; + symbol: string; + }; + priceNative: string; + priceUsd: string; + txns: { + m5: { buys: number; sells: number }; + h1: { buys: number; sells: number }; + h6: { buys: number; sells: number }; + h24: { buys: number; sells: number }; + }; + volume: { + h24: number; + h6: number; + h1: number; + m5: number; + }; + priceChange: { + m5: number; + h1: number; + h6: number; + h24: number; + }; + liquidity: { + usd: number; + base: number; + quote: number; + }; + fdv: number; + marketCap: number; + pairCreatedAt: number; + info: { + imageUrl: string; + websites: { label: string; url: string }[]; + socials: { type: string; url: string }[]; + }; + boosts: { + active: number; + }; +} + +export interface DexScreenerData { + schemaVersion: string; + pairs: DexScreenerPair[]; +} + +export interface Prices { + solana: { usd: string }; + bitcoin: { usd: string }; + ethereum: { usd: string }; +} + +export interface CalculatedBuyAmounts { + none: 0; + low: number; + medium: number; + high: number; +} diff --git a/packages/plugin-solana-agentkit/tsconfig.json b/packages/plugin-solana-agentkit/tsconfig.json new file mode 100644 index 0000000000..73993deaaf --- /dev/null +++ b/packages/plugin-solana-agentkit/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/plugin-solana-agentkit/tsup.config.ts b/packages/plugin-solana-agentkit/tsup.config.ts new file mode 100644 index 0000000000..dd25475bb6 --- /dev/null +++ b/packages/plugin-solana-agentkit/tsup.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "safe-buffer", + "base-x", + "bs58", + "borsh", + "@solana/buffer-layout", + "stream", + "buffer", + "querystring", + "amqplib", + // Add other modules you want to externalize + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e979dca71..4a35365e5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -186,6 +186,9 @@ importers: '@elizaos/plugin-solana': specifier: workspace:* version: link:../packages/plugin-solana + '@elizaos/plugin-solana-agentkit': + specifier: workspace:* + version: link:../packages/plugin-solana-agentkit '@elizaos/plugin-starknet': specifier: workspace:* version: link:../packages/plugin-starknet @@ -796,7 +799,7 @@ importers: version: 1.0.15 langchain: specifier: 0.3.6 - version: 0.3.6(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) + version: 0.3.6(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) ollama-ai-provider: specifier: 0.16.1 version: 0.16.1(zod@3.23.8) @@ -1545,6 +1548,60 @@ importers: specifier: 7.1.0 version: 7.1.0 + packages/plugin-solana-agentkit: + dependencies: + '@coral-xyz/anchor': + specifier: 0.30.1 + version: 0.30.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@elizaos/core': + specifier: workspace:* + version: link:../core + '@elizaos/plugin-tee': + specifier: workspace:* + version: link:../plugin-tee + '@elizaos/plugin-trustdb': + specifier: workspace:* + version: link:../plugin-trustdb + '@solana/spl-token': + specifier: 0.4.9 + version: 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': + specifier: 1.95.8 + version: 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + bignumber: + specifier: 1.1.0 + version: 1.1.0 + bignumber.js: + specifier: 9.1.2 + version: 9.1.2 + bs58: + specifier: 6.0.0 + version: 6.0.0 + fomo-sdk-solana: + specifier: 1.3.2 + version: 1.3.2(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + form-data: + specifier: 4.0.1 + version: 4.0.1 + node-cache: + specifier: 5.1.2 + version: 5.1.2 + pumpdotfun-sdk: + specifier: 1.3.2 + version: 1.3.2(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.28.1)(typescript@5.6.3)(utf-8-validate@5.0.10) + solana-agent-kit: + specifier: ^1.2.0 + version: 1.2.0(@noble/hashes@1.6.1)(axios@1.7.9)(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(handlebars@4.7.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1) + vitest: + specifier: 2.1.4 + version: 2.1.4(@types/node@22.10.2)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + packages/plugin-starknet: dependencies: '@avnu/avnu-sdk': @@ -2987,6 +3044,16 @@ packages: bs58: ^6.0.0 viem: ^2.21.0 + '@bonfida/sns-records@0.0.1': + resolution: {integrity: sha512-i28w9+BMFufhhpmLQCNx1CKKXTsEn+5RT18VFpPqdGO3sqaYlnUWC1m3wDpOvlzGk498dljgRpRo5wmcsnuEMg==} + peerDependencies: + '@solana/web3.js': ^1.87.3 + + '@bonfida/spl-name-service@3.0.7': + resolution: {integrity: sha512-okOLXhy+fQoyQ/sZgMleO5RrIZfTkWEoHMxWgUqg6RP/MTBlrKxlhKC6ymKn4UUe0C5s3Nb8A+3Ams7vX0nMDg==} + peerDependencies: + '@solana/web3.js': ^1.87.3 + '@braintree/sanitize-url@7.1.0': resolution: {integrity: sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==} @@ -4714,6 +4781,27 @@ packages: resolution: {integrity: sha512-6RUQHEp8wv+JwtYIIEBYBzbLlcAQZFc7EDOgAM0ukExjh9HiXoJzoWpgMRRCrr/koIbtwXPJUqBprZK1I1CXHQ==} engines: {node: '>=18'} + '@langchain/groq@0.1.2': + resolution: {integrity: sha512-bgQ9yGoNHOwG6LG2ngGvSNxF/1U1c1u3vKmFWmzecFIcBoQQOJY0jb0MrL3g1uTife0Sr3zxkWKXQg2aK/U4Sg==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.21 <0.4.0' + + '@langchain/langgraph-checkpoint@0.0.13': + resolution: {integrity: sha512-amdmBcNT8a9xP2VwcEWxqArng4gtRDcnVyVI4DsQIo1Aaz8e8+hH17zSwrUF3pt1pIYztngIfYnBOim31mtKMg==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.31 <0.4.0' + + '@langchain/langgraph-sdk@0.0.32': + resolution: {integrity: sha512-KQyM9kLO7T6AxwNrceajH7JOybP3pYpvUPnhiI2rrVndI1WyZUJ1eVC1e722BVRAPi6o+WcoTT4uMSZVinPOtA==} + + '@langchain/langgraph@0.2.36': + resolution: {integrity: sha512-zxk7ZCVxP0/Ut9785EiXCS7BE7sXd8cu943mcZUF2aNFUaQRTBbbiKpNdR3nb1+xO/B+HVktrJT2VFdkAywnng==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.36 <0.3.0 || >=0.3.9 < 0.4.0' + '@langchain/openai@0.3.16': resolution: {integrity: sha512-Om9HRlTeI0Ou6D4pfxbWHop4WGfkCdV/7v1W/+Jr7NSf0BNoA9jk5GqGms8ZtOYSGgPvizDu3i0TrM3B4cN4NA==} engines: {node: '>=18'} @@ -4806,6 +4894,14 @@ packages: '@lifi/types@16.3.0': resolution: {integrity: sha512-rYMdXRdNOyJb5tI5CXfqxU4k62GiJrElx0DEZ8ZRFYFtljg69X6hrMKER1wVWkRpcB67Ca8SKebLnufy7qCaTw==} + '@lightprotocol/compressed-token@0.17.1': + resolution: {integrity: sha512-493KCmZGw1BcHVRJaeRm8EEs+L7gX8dwY7JG13w2pfgOMtZXZ7Wxt261jFJxQJzRLTrUSlrbRJOmfW1+S1Y8SQ==} + peerDependencies: + '@lightprotocol/stateless.js': 0.17.1 + + '@lightprotocol/stateless.js@0.17.1': + resolution: {integrity: sha512-EjId1n33A6dBwpce33Wsa/fs/CDKtMtRrkxbApH0alXrnEXmbW6QhIViXOrKYXjZ4uJQM1xsBtsKe0vqJ4nbtQ==} + '@lit-labs/ssr-dom-shim@1.2.1': resolution: {integrity: sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==} @@ -4878,6 +4974,12 @@ packages: resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==} engines: {node: '>=12.0.0'} + '@metaplex-foundation/mpl-core@1.1.1': + resolution: {integrity: sha512-h1kLw+cGaV8SiykoHDb1/G01+VYqtJXAt0uGuO5+2Towsdtc6ET4M62iqUnh4EacTVMIW1yYHsKsG/LYWBCKaA==} + peerDependencies: + '@metaplex-foundation/umi': '>=0.8.2 < 1' + '@noble/hashes': ^1.3.1 + '@metaplex-foundation/mpl-token-metadata@3.3.0': resolution: {integrity: sha512-t5vO8Wr3ZZZPGrVrGNcosX5FMkwQSgBiVMQMRNDG2De7voYFJmIibD5jdG05EoQ4Y5kZVEiwhYaO+wJB3aO5AA==} peerDependencies: @@ -5748,6 +5850,22 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@orca-so/common-sdk@0.6.4': + resolution: {integrity: sha512-iOiC6exTA9t2CEOaUPoWlNP3soN/1yZFjoz1mSf7NvOqo/PJZeIdWpB7BRXwU0mGGatjxU4SFgMGQ8NrSx+ONw==} + peerDependencies: + '@solana/spl-token': ^0.4.1 + '@solana/web3.js': ^1.90.0 + decimal.js: ^10.4.3 + + '@orca-so/whirlpools-sdk@0.13.12': + resolution: {integrity: sha512-+LOqGTe0DYUsYwemltOU4WQIviqoICQlIcAmmEX/WnBh6wntpcLDcXkPV6dBHW7NA2/J8WEVAZ50biLJb4subg==} + peerDependencies: + '@coral-xyz/anchor': ~0.29.0 + '@orca-so/common-sdk': 0.6.4 + '@solana/spl-token': ^0.4.8 + '@solana/web3.js': ^1.90.0 + decimal.js: ^10.4.3 + '@parcel/watcher-android-arm64@2.5.0': resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} engines: {node: '>= 10.0.0'} @@ -6188,6 +6306,9 @@ packages: '@raydium-io/raydium-sdk-v2@0.1.82-alpha': resolution: {integrity: sha512-PScLnWZV5Y/igcvP4hbD/1ztzW2w5a2YStolu9A5VT6uB2q+izeo+SE7IqzZggyaReXyisjdkNGpB/kMdkdJGQ==} + '@raydium-io/raydium-sdk-v2@0.1.95-alpha': + resolution: {integrity: sha512-+u7yxo/R1JDysTCzOuAlh90ioBe2DlM2Hbcz/tFsxP/YzmnYQzShvNjcmc0361a4zJhmlrEJfpFXW0J3kkX5vA==} + '@react-icons/all-files@4.1.0': resolution: {integrity: sha512-hxBI2UOuVaI3O/BhQfhtb4kcGn9ft12RWAFVMUeNjqqhLsHvFtzIkFaptBJpFDANTKoDfdVoHTKZDlwKCACbMQ==} peerDependencies: @@ -6855,6 +6976,11 @@ packages: '@solana/codecs-core@2.0.0-preview.2': resolution: {integrity: sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==} + '@solana/codecs-core@2.0.0-preview.4': + resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-core@2.0.0-rc.1': resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} peerDependencies: @@ -6863,6 +6989,11 @@ packages: '@solana/codecs-data-structures@2.0.0-preview.2': resolution: {integrity: sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==} + '@solana/codecs-data-structures@2.0.0-preview.4': + resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-data-structures@2.0.0-rc.1': resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} peerDependencies: @@ -6871,6 +7002,11 @@ packages: '@solana/codecs-numbers@2.0.0-preview.2': resolution: {integrity: sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==} + '@solana/codecs-numbers@2.0.0-preview.4': + resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-numbers@2.0.0-rc.1': resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} peerDependencies: @@ -6881,6 +7017,12 @@ packages: peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 + '@solana/codecs-strings@2.0.0-preview.4': + resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5' + '@solana/codecs-strings@2.0.0-rc.1': resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} peerDependencies: @@ -6890,6 +7032,11 @@ packages: '@solana/codecs@2.0.0-preview.2': resolution: {integrity: sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==} + '@solana/codecs@2.0.0-preview.4': + resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==} + peerDependencies: + typescript: '>=5' + '@solana/codecs@2.0.0-rc.1': resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} peerDependencies: @@ -6899,6 +7046,12 @@ packages: resolution: {integrity: sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==} hasBin: true + '@solana/errors@2.0.0-preview.4': + resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==} + hasBin: true + peerDependencies: + typescript: '>=5' + '@solana/errors@2.0.0-rc.1': resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} hasBin: true @@ -6908,6 +7061,11 @@ packages: '@solana/options@2.0.0-preview.2': resolution: {integrity: sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==} + '@solana/options@2.0.0-preview.4': + resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==} + peerDependencies: + typescript: '>=5' + '@solana/options@2.0.0-rc.1': resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: @@ -6919,6 +7077,12 @@ packages: peerDependencies: '@solana/web3.js': ^1.91.6 + '@solana/spl-token-group@0.0.5': + resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.94.0 + '@solana/spl-token-group@0.0.7': resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} engines: {node: '>=16'} @@ -6937,6 +7101,12 @@ packages: peerDependencies: '@solana/web3.js': ^1.91.6 + '@solana/spl-token@0.4.8': + resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.94.0 + '@solana/spl-token@0.4.9': resolution: {integrity: sha512-g3wbj4F4gq82YQlwqhPB0gHFXfgsC6UmyGMxtSLf/BozT/oKd59465DbnlUK8L8EcimKMavxsVAMoLcEdeCicg==} engines: {node: '>=16'} @@ -6957,6 +7127,9 @@ packages: resolution: {integrity: sha512-tUd9srDLkRpe1BYg7we+c4UhRQkq+XQWswsr/L1xfGmoRDF47BPSXf4zE7ZU2GRBGvxtGt7lwJVAufQyQYhxTQ==} engines: {node: '>=16'} + '@solana/web3.js@1.95.3': + resolution: {integrity: sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og==} + '@solana/web3.js@1.95.5': resolution: {integrity: sha512-hU9cBrbg1z6gEjLH9vwIckGBVB78Ijm0iZFNk4ocm5OD82piPwuk3MeQ1rfiKD9YQtr95krrcaopb49EmQJlRg==} @@ -8716,6 +8889,9 @@ packages: base-x@3.0.10: resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + base-x@4.0.0: + resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + base-x@5.0.0: resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} @@ -8868,6 +9044,9 @@ packages: borsh@1.0.0: resolution: {integrity: sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ==} + borsh@2.0.0: + resolution: {integrity: sha512-kc9+BgR3zz9+cjbwM8ODoUB4fs3X3I5A/HtX7LZKxCLaMrEeDFoBpnhZY//DTS1VZBSs6S5v46RZRbZjRFspEg==} + bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} @@ -8952,6 +9131,9 @@ packages: bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + bs58@6.0.0: resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} @@ -11702,6 +11884,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphemesplit@2.4.4: + resolution: {integrity: sha512-lKrpp1mk1NH26USxC/Asw4OHbhSQf5XfrWZ+CDv/dFVvd1j17kFgMotdJvOesmHkbFX9P9sBfpH8VogxOWLg8w==} + graphql-request@6.1.0: resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} peerDependencies: @@ -11721,6 +11906,9 @@ packages: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} + groq-sdk@0.5.0: + resolution: {integrity: sha512-RVmhW7qZ+XZoy5fIuSdx/LGQJONpL8MHgZEW7dFwTdgkzStub2XQx6OKv28CHogijdwH41J+Npj/z2jBPu3vmw==} + gtoken@7.1.0: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} @@ -16939,6 +17127,10 @@ packages: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + solana-agent-kit@1.2.0: + resolution: {integrity: sha512-JdNXRIKsKsz8nPuCOLdSMQ6sejqwafClJBaQL1eGHU7cpyJZbTLHatD/VFpO2lv+nr6Sqg+G05mtCRyV0ELc0Q==} + engines: {node: '>=23.1.0', pnpm: '>=8.0.0'} + solc@0.8.26: resolution: {integrity: sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==} engines: {node: '>=10.0.0'} @@ -17513,6 +17705,9 @@ packages: tiny-emitter@2.1.0: resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -18045,6 +18240,9 @@ packages: resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} engines: {node: '>=4'} + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -20970,6 +21168,32 @@ snapshots: bs58: 6.0.0 viem: 2.21.53(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@bonfida/sns-records@0.0.1(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + borsh: 1.0.0 + bs58: 5.0.0 + buffer: 6.0.3 + + '@bonfida/spl-name-service@3.0.7(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@bonfida/sns-records': 0.0.1(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@noble/curves': 1.7.0 + '@scure/base': 1.2.1 + '@solana/spl-token': 0.4.6(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + borsh: 2.0.0 + buffer: 6.0.3 + graphemesplit: 2.4.4 + ipaddr.js: 2.2.0 + punycode: 2.3.1 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@braintree/sanitize-url@7.1.0': {} '@cfworker/json-schema@4.0.3': {} @@ -23570,6 +23794,64 @@ snapshots: transitivePeerDependencies: - openai + '@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))': + dependencies: + '@cfworker/json-schema': 4.0.3 + ansi-styles: 5.2.0 + camelcase: 6.3.0 + decamelize: 1.2.0 + js-tiktoken: 1.0.15 + langsmith: 0.2.13(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + mustache: 4.2.0 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 10.0.0 + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + transitivePeerDependencies: + - openai + + '@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)': + dependencies: + '@langchain/core': 0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/openai': 0.3.16(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + groq-sdk: 0.5.0(encoding@0.1.13) + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + transitivePeerDependencies: + - encoding + optional: true + + '@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)': + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/openai': 0.3.16(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + groq-sdk: 0.5.0(encoding@0.1.13) + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + transitivePeerDependencies: + - encoding + + '@langchain/langgraph-checkpoint@0.0.13(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))': + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + uuid: 10.0.0 + + '@langchain/langgraph-sdk@0.0.32': + dependencies: + '@types/json-schema': 7.0.15 + p-queue: 6.6.2 + p-retry: 4.6.2 + uuid: 9.0.1 + + '@langchain/langgraph@0.2.36(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))': + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/langgraph-checkpoint': 0.0.13(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))) + '@langchain/langgraph-sdk': 0.0.32 + uuid: 10.0.0 + zod: 3.23.8 + '@langchain/openai@0.3.16(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)': dependencies: '@langchain/core': 0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) @@ -23580,11 +23862,26 @@ snapshots: transitivePeerDependencies: - encoding + '@langchain/openai@0.3.16(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)': + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + js-tiktoken: 1.0.15 + openai: 4.77.0(encoding@0.1.13)(zod@3.23.8) + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + transitivePeerDependencies: + - encoding + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))': dependencies: '@langchain/core': 0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) js-tiktoken: 1.0.15 + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))': + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + js-tiktoken: 1.0.15 + '@leichtgewicht/ip-codec@2.0.5': {} '@lens-protocol/blockchain-bindings@0.10.2(@jest/globals@29.7.0)(bufferutil@4.0.8)(utf-8-validate@5.0.10)': @@ -23825,6 +24122,34 @@ snapshots: '@lifi/types@16.3.0': {} + '@lightprotocol/compressed-token@0.17.1(@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@lightprotocol/stateless.js': 0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + buffer: 6.0.3 + tweetnacl: 1.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@noble/hashes': 1.5.0 + '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + buffer: 6.0.3 + superstruct: 2.0.2 + tweetnacl: 1.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + '@lit-labs/ssr-dom-shim@1.2.1': {} '@lit-protocol/access-control-conditions@2.1.62(bufferutil@4.0.8)(utf-8-validate@5.0.10)': @@ -24064,6 +24389,12 @@ snapshots: tweetnacl: 1.0.3 tweetnacl-util: 0.15.1 + '@metaplex-foundation/mpl-core@1.1.1(@metaplex-foundation/umi@0.9.2)(@noble/hashes@1.6.1)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@msgpack/msgpack': 3.0.0-beta2 + '@noble/hashes': 1.6.1 + '@metaplex-foundation/mpl-token-metadata@3.3.0(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/mpl-toolbox': 0.9.4(@metaplex-foundation/umi@0.9.2) @@ -24088,6 +24419,21 @@ snapshots: transitivePeerDependencies: - encoding + '@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(encoding@0.1.13)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-downloader-http': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-http-fetch': 0.9.2(@metaplex-foundation/umi@0.9.2)(encoding@0.1.13) + '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-rpc-chunk-get-accounts': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-serializer-data-view': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - encoding + '@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 @@ -24099,6 +24445,13 @@ snapshots: '@noble/curves': 1.7.0 '@solana/web3.js': 1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@noble/curves': 1.7.0 + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2)(encoding@0.1.13)': dependencies: '@metaplex-foundation/umi': 0.9.2 @@ -24126,6 +24479,12 @@ snapshots: '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 @@ -24154,12 +24513,24 @@ snapshots: '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 '@solana/web3.js': 1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) buffer: 6.0.3 + '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + buffer: 6.0.3 + '@metaplex-foundation/umi@0.9.2': dependencies: '@metaplex-foundation/umi-options': 0.8.9 @@ -25338,6 +25709,22 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@orca-so/common-sdk@0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3)': + dependencies: + '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + decimal.js: 10.4.3 + tiny-invariant: 1.3.3 + + '@orca-so/whirlpools-sdk@0.13.12(@coral-xyz/anchor@0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@orca-so/common-sdk@0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3))(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3)': + dependencies: + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@orca-so/common-sdk': 0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3) + '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + decimal.js: 10.4.3 + tiny-invariant: 1.3.3 + '@parcel/watcher-android-arm64@2.5.0': optional: true @@ -25788,6 +26175,28 @@ snapshots: - typescript - utf-8-validate + '@raydium-io/raydium-sdk-v2@0.1.95-alpha(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + axios: 1.7.9(debug@4.4.0) + big.js: 6.2.2 + bn.js: 5.2.1 + dayjs: 1.11.13 + decimal.js-light: 2.5.1 + jsonfile: 6.1.0 + lodash: 4.17.21 + toformat: 2.0.0 + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@react-icons/all-files@4.1.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -26662,6 +27071,11 @@ snapshots: dependencies: '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-core@2.0.0-preview.4(typescript@5.6.3)': + dependencies: + '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) + typescript: 5.6.3 + '@solana/codecs-core@2.0.0-rc.1(typescript@5.6.3)': dependencies: '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) @@ -26673,6 +27087,13 @@ snapshots: '@solana/codecs-numbers': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-data-structures@2.0.0-preview.4(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) + '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) + typescript: 5.6.3 + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.6.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) @@ -26685,6 +27106,12 @@ snapshots: '@solana/codecs-core': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-numbers@2.0.0-preview.4(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) + '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) + typescript: 5.6.3 + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.6.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) @@ -26698,6 +27125,14 @@ snapshots: '@solana/errors': 2.0.0-preview.2 fastestsmallesttextencoderdecoder: 1.0.22 + '@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) + '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.6.3 + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) @@ -26716,6 +27151,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) @@ -26732,6 +27178,12 @@ snapshots: chalk: 5.3.0 commander: 12.1.0 + '@solana/errors@2.0.0-preview.4(typescript@5.6.3)': + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + typescript: 5.6.3 + '@solana/errors@2.0.0-rc.1(typescript@5.6.3)': dependencies: chalk: 5.3.0 @@ -26743,6 +27195,17 @@ snapshots: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 + '@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) + '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) @@ -26762,6 +27225,15 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/spl-type-length-value': 0.1.0 + '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) @@ -26770,6 +27242,14 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) @@ -26793,6 +27273,21 @@ snapshots: - typescript - utf-8-validate + '@solana/spl-token@0.4.8(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.5(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + buffer: 6.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -26825,6 +27320,28 @@ snapshots: '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 + '@solana/web3.js@1.95.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.26.0 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@solana/buffer-layout': 4.0.1 + agentkeepalive: 4.5.0 + bigint-buffer: 1.1.5 + bn.js: 5.2.1 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.1.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) + node-fetch: 2.7.0(encoding@0.1.13) + rpc-websockets: 9.0.4 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + '@solana/web3.js@1.95.5(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.26.0 @@ -29241,6 +29758,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + base-x@4.0.0: {} + base-x@5.0.0: {} base64-arraybuffer@0.2.0: {} @@ -29424,6 +29943,8 @@ snapshots: borsh@1.0.0: {} + borsh@2.0.0: {} + bottleneck@2.19.5: {} bowser@2.11.0: {} @@ -29608,6 +30129,10 @@ snapshots: dependencies: base-x: 3.0.10 + bs58@5.0.0: + dependencies: + base-x: 4.0.0 + bs58@6.0.0: dependencies: base-x: 5.0.0 @@ -33077,6 +33602,11 @@ snapshots: graphemer@1.4.0: {} + graphemesplit@2.4.4: + dependencies: + js-base64: 3.7.7 + unicode-trie: 2.0.0 + graphql-request@6.1.0(encoding@0.1.13)(graphql@16.10.0): dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) @@ -33099,6 +33629,19 @@ snapshots: section-matter: 1.0.0 strip-bom-string: 1.0.0 + groq-sdk@0.5.0(encoding@0.1.13): + dependencies: + '@types/node': 18.19.68 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.5.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + web-streams-polyfill: 3.3.3 + transitivePeerDependencies: + - encoding + gtoken@7.1.0(encoding@0.1.13): dependencies: gaxios: 6.7.1(encoding@0.1.13) @@ -35059,7 +35602,7 @@ snapshots: inherits: 2.0.4 stream-splicer: 2.0.1 - langchain@0.3.6(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)): + langchain@0.3.6(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)): dependencies: '@langchain/core': 0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) '@langchain/openai': 0.3.16(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -35075,6 +35618,30 @@ snapshots: zod: 3.23.8 zod-to-json-schema: 3.24.1(zod@3.23.8) optionalDependencies: + '@langchain/groq': 0.1.2(@langchain/core@0.3.26(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + axios: 1.7.9(debug@4.4.0) + handlebars: 4.7.8 + transitivePeerDependencies: + - encoding + - openai + + langchain@0.3.6(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)): + dependencies: + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/openai': 0.3.16(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))) + js-tiktoken: 1.0.15 + js-yaml: 4.1.0 + jsonpointer: 5.0.1 + langsmith: 0.2.13(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + openapi-types: 12.1.3 + p-retry: 4.6.2 + uuid: 10.0.0 + yaml: 2.6.1 + zod: 3.23.8 + zod-to-json-schema: 3.24.1(zod@3.23.8) + optionalDependencies: + '@langchain/groq': 0.1.2(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) axios: 1.7.9(debug@4.4.0) handlebars: 4.7.8 transitivePeerDependencies: @@ -35100,6 +35667,17 @@ snapshots: optionalDependencies: openai: 4.73.0(encoding@0.1.13)(zod@3.23.8) + langsmith@0.2.13(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)): + dependencies: + '@types/uuid': 10.0.0 + commander: 10.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + semver: 7.6.3 + uuid: 10.0.0 + optionalDependencies: + openai: 4.77.0(encoding@0.1.13)(zod@3.23.8) + latest-version@7.0.0: dependencies: package-json: 8.1.1 @@ -39962,6 +40540,56 @@ snapshots: ip-address: 9.0.5 smart-buffer: 4.2.0 + solana-agent-kit@1.2.0(@noble/hashes@1.6.1)(axios@1.7.9)(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(handlebars@4.7.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8): + dependencies: + '@bonfida/spl-name-service': 3.0.7(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@langchain/core': 0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + '@langchain/groq': 0.1.2(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + '@langchain/langgraph': 0.2.36(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))) + '@langchain/openai': 0.3.16(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) + '@lightprotocol/compressed-token': 0.17.1(@lightprotocol/stateless.js@0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@lightprotocol/stateless.js': 0.17.1(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@metaplex-foundation/mpl-core': 1.1.1(@metaplex-foundation/umi@0.9.2)(@noble/hashes@1.6.1) + '@metaplex-foundation/mpl-token-metadata': 3.3.0(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-bundle-defaults': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(encoding@0.1.13) + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@orca-so/common-sdk': 0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3) + '@orca-so/whirlpools-sdk': 0.13.12(@coral-xyz/anchor@0.29.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(@orca-so/common-sdk@0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3))(@solana/spl-token@0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(decimal.js@10.4.3) + '@raydium-io/raydium-sdk-v2': 0.1.95-alpha(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.8(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + bn.js: 5.2.1 + bs58: 6.0.0 + decimal.js: 10.4.3 + dotenv: 16.4.7 + form-data: 4.0.1 + langchain: 0.3.6(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(@langchain/groq@0.1.2(@langchain/core@0.3.26(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.77.0(encoding@0.1.13)(zod@3.23.8)) + openai: 4.77.0(encoding@0.1.13)(zod@3.23.8) + typedoc: 0.26.11(typescript@5.6.3) + transitivePeerDependencies: + - '@langchain/anthropic' + - '@langchain/aws' + - '@langchain/cohere' + - '@langchain/google-genai' + - '@langchain/google-vertexai' + - '@langchain/mistralai' + - '@langchain/ollama' + - '@noble/hashes' + - axios + - bufferutil + - cheerio + - debug + - encoding + - fastestsmallesttextencoderdecoder + - handlebars + - peggy + - typeorm + - typescript + - utf-8-validate + - zod + solc@0.8.26(debug@4.4.0): dependencies: command-exists: 1.2.9 @@ -40625,6 +41253,8 @@ snapshots: tiny-emitter@2.1.0: {} + tiny-inflate@1.0.3: {} + tiny-invariant@1.3.3: {} tiny-warning@1.0.3: {} @@ -41208,6 +41838,11 @@ snapshots: unicode-property-aliases-ecmascript@2.1.0: {} + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + unicorn-magic@0.1.0: {} unified@11.0.5: From 7a34cd14e356019b3ebb9739260cadf9cf0e430a Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 20:06:19 +0800 Subject: [PATCH 02/58] fix: clean up --- .../src/evaluators/trust.ts | 543 -------- packages/plugin-solana-agentkit/src/index.ts | 20 +- .../src/providers/orderBook.ts | 45 - .../src/providers/simulationSellingService.ts | 501 -------- .../src/providers/token.ts | 1124 ----------------- .../src/providers/tokenUtils.ts | 72 -- .../src/providers/trustScoreProvider.ts | 740 ----------- .../src/providers/wallet.ts | 391 ------ 8 files changed, 4 insertions(+), 3432 deletions(-) delete mode 100644 packages/plugin-solana-agentkit/src/evaluators/trust.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/orderBook.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/token.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/tokenUtils.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts delete mode 100644 packages/plugin-solana-agentkit/src/providers/wallet.ts diff --git a/packages/plugin-solana-agentkit/src/evaluators/trust.ts b/packages/plugin-solana-agentkit/src/evaluators/trust.ts deleted file mode 100644 index 2c4f441cf5..0000000000 --- a/packages/plugin-solana-agentkit/src/evaluators/trust.ts +++ /dev/null @@ -1,543 +0,0 @@ -import { - composeContext, - generateObjectArray, - generateTrueOrFalse, - MemoryManager, - booleanFooter, - ActionExample, - Content, - IAgentRuntime, - Memory, - ModelClass, - Evaluator, -} from "@elizaos/core"; -import { TrustScoreManager } from "../providers/trustScoreProvider.ts"; -import { TokenProvider } from "../providers/token.ts"; -import { WalletProvider } from "../providers/wallet.ts"; -import { TrustScoreDatabase } from "@elizaos/plugin-trustdb"; -import { Connection } from "@solana/web3.js"; -import { getWalletKey } from "../keypairUtils.ts"; - -const shouldProcessTemplate = - `# Task: Decide if the recent messages should be processed for token recommendations. - - Look for messages that: - - Mention specific token tickers or contract addresses - - Contain words related to buying, selling, or trading tokens - - Express opinions or convictions about tokens - - Based on the following conversation, should the messages be processed for recommendations? YES or NO - - {{recentMessages}} - - Should the messages be processed for recommendations? ` + booleanFooter; - -export const formatRecommendations = (recommendations: Memory[]) => { - const messageStrings = recommendations - .reverse() - .map((rec: Memory) => `${(rec.content as Content)?.content}`); - const finalMessageStrings = messageStrings.join("\n"); - return finalMessageStrings; -}; - -const recommendationTemplate = `TASK: Extract recommendations to buy or sell memecoins from the conversation as an array of objects in JSON format. - - Memecoins usually have a ticker and a contract address. Additionally, recommenders may make recommendations with some amount of conviction. The amount of conviction in their recommendation can be none, low, medium, or high. Recommenders can make recommendations to buy, not buy, sell and not sell. - -# START OF EXAMPLES -These are an examples of the expected output of this task: -{{evaluationExamples}} -# END OF EXAMPLES - -# INSTRUCTIONS - -Extract any new recommendations from the conversation that are not already present in the list of known recommendations below: -{{recentRecommendations}} - -- Include the recommender's username -- Try not to include already-known recommendations. If you think a recommendation is already known, but you're not sure, respond with alreadyKnown: true. -- Set the conviction to 'none', 'low', 'medium' or 'high' -- Set the recommendation type to 'buy', 'dont_buy', 'sell', or 'dont_sell' -- Include the contract address and/or ticker if available - -Recent Messages: -{{recentMessages}} - -Response should be a JSON object array inside a JSON markdown block. Correct response format: -\`\`\`json -[ - { - "recommender": string, - "ticker": string | null, - "contractAddress": string | null, - "type": enum, - "conviction": enum, - "alreadyKnown": boolean - }, - ... -] -\`\`\``; - -async function handler(runtime: IAgentRuntime, message: Memory) { - console.log("Evaluating for trust"); - const state = await runtime.composeState(message); - - const { agentId, roomId } = state; - - // Check if we should process the messages - const shouldProcessContext = composeContext({ - state, - template: shouldProcessTemplate, - }); - - const shouldProcess = await generateTrueOrFalse({ - context: shouldProcessContext, - modelClass: ModelClass.SMALL, - runtime, - }); - - if (!shouldProcess) { - console.log("Skipping process"); - return []; - } - - console.log("Processing recommendations"); - - // Get recent recommendations - const recommendationsManager = new MemoryManager({ - runtime, - tableName: "recommendations", - }); - - const recentRecommendations = await recommendationsManager.getMemories({ - roomId, - count: 20, - }); - - const context = composeContext({ - state: { - ...state, - recentRecommendations: formatRecommendations(recentRecommendations), - }, - template: recommendationTemplate, - }); - - const recommendations = await generateObjectArray({ - runtime, - context, - modelClass: ModelClass.LARGE, - }); - - console.log("recommendations", recommendations); - - if (!recommendations) { - return []; - } - - // If the recommendation is already known or corrupted, remove it - const filteredRecommendations = recommendations.filter((rec) => { - return ( - !rec.alreadyKnown && - (rec.ticker || rec.contractAddress) && - rec.recommender && - rec.conviction && - rec.recommender.trim() !== "" - ); - }); - - const { publicKey } = await getWalletKey(runtime, false); - - for (const rec of filteredRecommendations) { - // create the wallet provider and token provider - const walletProvider = new WalletProvider( - new Connection( - runtime.getSetting("RPC_URL") || - "https://api.mainnet-beta.solana.com" - ), - publicKey - ); - const tokenProvider = new TokenProvider( - rec.contractAddress, - walletProvider, - runtime.cacheManager - ); - - // TODO: Check to make sure the contract address is valid, it's the right one, etc - - // - if (!rec.contractAddress) { - const tokenAddress = await tokenProvider.getTokenFromWallet( - runtime, - rec.ticker - ); - rec.contractAddress = tokenAddress; - if (!tokenAddress) { - // try to search for the symbol and return the contract address with they highest liquidity and market cap - const result = await tokenProvider.searchDexScreenerData( - rec.ticker - ); - const tokenAddress = result?.baseToken?.address; - rec.contractAddress = tokenAddress; - if (!tokenAddress) { - console.warn("Could not find contract address for token"); - continue; - } - } - } - - // create the trust score manager - - const trustScoreDb = new TrustScoreDatabase(runtime.databaseAdapter.db); - const trustScoreManager = new TrustScoreManager( - runtime, - tokenProvider, - trustScoreDb - ); - - // get actors from the database - const participants = - await runtime.databaseAdapter.getParticipantsForRoom( - message.roomId - ); - - // find the first user Id from a user with the username that we extracted - const user = participants.find(async (actor) => { - const user = await runtime.databaseAdapter.getAccountById(actor); - return ( - user.name.toLowerCase().trim() === - rec.recommender.toLowerCase().trim() - ); - }); - - if (!user) { - console.warn("Could not find user: ", rec.recommender); - continue; - } - - const account = await runtime.databaseAdapter.getAccountById(user); - const userId = account.id; - - const recMemory = { - userId, - agentId, - content: { text: JSON.stringify(rec) }, - roomId, - createdAt: Date.now(), - }; - - await recommendationsManager.createMemory(recMemory, true); - - console.log("recommendationsManager", rec); - - // - from here we just need to make sure code is right - - // buy, dont buy, sell, dont sell - - const buyAmounts = await tokenProvider.calculateBuyAmounts(); - - let buyAmount = buyAmounts[rec.conviction.toLowerCase().trim()]; - if (!buyAmount) { - // handle annoying cases - // for now just put in 10 sol - buyAmount = 10; - } - - // TODO: is this is a buy, sell, dont buy, or dont sell? - const shouldTrade = await tokenProvider.shouldTradeToken(); - - if (!shouldTrade) { - console.warn( - "There might be a problem with the token, not trading" - ); - continue; - } - - switch (rec.type) { - case "buy": - // for now, lets just assume buy only, but we should implement - await trustScoreManager.createTradePerformance( - runtime, - rec.contractAddress, - userId, - { - buy_amount: rec.buyAmount, - is_simulation: true, - } - ); - break; - case "sell": - case "dont_sell": - case "dont_buy": - console.warn("Not implemented"); - break; - } - } - - return filteredRecommendations; -} - -export const trustEvaluator: Evaluator = { - name: "EXTRACT_RECOMMENDATIONS", - similes: [ - "GET_RECOMMENDATIONS", - "EXTRACT_TOKEN_RECS", - "EXTRACT_MEMECOIN_RECS", - ], - alwaysRun: true, - validate: async ( - runtime: IAgentRuntime, - message: Memory - ): Promise => { - if (message.content.text.length < 5) { - return false; - } - - return message.userId !== message.agentId; - }, - description: - "Extract recommendations to buy or sell memecoins/tokens from the conversation, including details like ticker, contract address, conviction level, and recommender username.", - handler, - examples: [ - { - context: `Actors in the scene: -{{user1}}: Experienced DeFi degen. Constantly chasing high yield farms. -{{user2}}: New to DeFi, learning the ropes. - -Recommendations about the actors: -None`, - messages: [ - { - user: "{{user1}}", - content: { - text: "Yo, have you checked out $SOLARUG? Dope new yield aggregator on Solana.", - }, - }, - { - user: "{{user2}}", - content: { - text: "Nah, I'm still trying to wrap my head around how yield farming even works haha. Is it risky?", - }, - }, - { - user: "{{user1}}", - content: { - text: "I mean, there's always risk in DeFi, but the $SOLARUG devs seem legit. Threw a few sol into the FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9 vault, farming's been smooth so far.", - }, - }, - ] as ActionExample[], - outcome: `\`\`\`json -[ - { - "recommender": "{{user1}}", - "ticker": "SOLARUG", - "contractAddress": "FCweoTfJ128jGgNEXgdfTXdEZVk58Bz9trCemr6sXNx9", - "type": "buy", - "conviction": "medium", - "alreadyKnown": false - } -] -\`\`\``, - }, - - { - context: `Actors in the scene: -{{user1}}: Solana maximalist. Believes Solana will flip Ethereum. -{{user2}}: Multichain proponent. Holds both SOL and ETH. - -Recommendations about the actors: -{{user1}} has previously promoted $COPETOKEN and $SOYLENT.`, - messages: [ - { - user: "{{user1}}", - content: { - text: "If you're not long $SOLVAULT at 7tRzKud6FBVFEhYqZS3CuQ2orLRM21bdisGykL5Sr4Dx, you're missing out. This will be the blackhole of Solana liquidity.", - }, - }, - { - user: "{{user2}}", - content: { - text: "Idk man, feels like there's a new 'vault' or 'reserve' token every week on Sol. What happened to $COPETOKEN and $SOYLENT that you were shilling before?", - }, - }, - { - user: "{{user1}}", - content: { - text: "$COPETOKEN and $SOYLENT had their time, I took profits near the top. But $SOLVAULT is different, it has actual utility. Do what you want, but don't say I didn't warn you when this 50x's and you're left holding your $ETH bags.", - }, - }, - ] as ActionExample[], - outcome: `\`\`\`json -[ - { - "recommender": "{{user1}}", - "ticker": "COPETOKEN", - "contractAddress": null, - "type": "sell", - "conviction": "low", - "alreadyKnown": true - }, - { - "recommender": "{{user1}}", - "ticker": "SOYLENT", - "contractAddress": null, - "type": "sell", - "conviction": "low", - "alreadyKnown": true - }, - { - "recommender": "{{user1}}", - "ticker": "SOLVAULT", - "contractAddress": "7tRzKud6FBVFEhYqZS3CuQ2orLRM21bdisGykL5Sr4Dx", - "type": "buy", - "conviction": "high", - "alreadyKnown": false - } -] -\`\`\``, - }, - - { - context: `Actors in the scene: -{{user1}}: Self-proclaimed Solana alpha caller. Allegedly has insider info. -{{user2}}: Degen gambler. Will ape into any hyped token. - -Recommendations about the actors: -None`, - messages: [ - { - user: "{{user1}}", - content: { - text: "I normally don't do this, but I like you anon, so I'll let you in on some alpha. $ROULETTE at 48vV5y4DRH1Adr1bpvSgFWYCjLLPtHYBqUSwNc2cmCK2 is going to absolutely send it soon. You didn't hear it from me 🤐", - }, - }, - { - user: "{{user2}}", - content: { - text: "Oh shit, insider info from the alpha god himself? Say no more, I'm aping in hard.", - }, - }, - ] as ActionExample[], - outcome: `\`\`\`json -[ - { - "recommender": "{{user1}}", - "ticker": "ROULETTE", - "contractAddress": "48vV5y4DRH1Adr1bpvSgFWYCjLLPtHYBqUSwNc2cmCK2", - "type": "buy", - "conviction": "high", - "alreadyKnown": false - } -] -\`\`\``, - }, - - { - context: `Actors in the scene: -{{user1}}: NFT collector and trader. Bullish on Solana NFTs. -{{user2}}: Only invests based on fundamentals. Sees all NFTs as worthless JPEGs. - -Recommendations about the actors: -None -`, - messages: [ - { - user: "{{user1}}", - content: { - text: "GM. I'm heavily accumulating $PIXELAPE, the token for the Pixel Ape Yacht Club NFT collection. 10x is inevitable.", - }, - }, - { - user: "{{user2}}", - content: { - text: "NFTs are a scam bro. There's no underlying value. You're essentially trading worthless JPEGs.", - }, - }, - { - user: "{{user1}}", - content: { - text: "Fun staying poor 🤡 $PIXELAPE is about to moon and you'll be left behind.", - }, - }, - { - user: "{{user2}}", - content: { - text: "Whatever man, I'm not touching that shit with a ten foot pole. Have fun holding your bags.", - }, - }, - { - user: "{{user1}}", - content: { - text: "Don't need luck where I'm going 😎 Once $PIXELAPE at 3hAKKmR6XyBooQBPezCbUMhrmcyTkt38sRJm2thKytWc takes off, you'll change your tune.", - }, - }, - ], - outcome: `\`\`\`json -[ - { - "recommender": "{{user1}}", - "ticker": "PIXELAPE", - "contractAddress": "3hAKKmR6XyBooQBPezCbUMhrmcyTkt38sRJm2thKytWc", - "type": "buy", - "conviction": "high", - "alreadyKnown": false - } -] -\`\`\``, - }, - - { - context: `Actors in the scene: -{{user1}}: Contrarian investor. Bets against hyped projects. -{{user2}}: Trend follower. Buys tokens that are currently popular. - -Recommendations about the actors: -None`, - messages: [ - { - user: "{{user2}}", - content: { - text: "$SAMOYED is the talk of CT right now. Making serious moves. Might have to get a bag.", - }, - }, - { - user: "{{user1}}", - content: { - text: "Whenever a token is the 'talk of CT', that's my cue to short it. $SAMOYED is going to dump hard, mark my words.", - }, - }, - { - user: "{{user2}}", - content: { - text: "Idk man, the hype seems real this time. 5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr chart looks bullish af.", - }, - }, - { - user: "{{user1}}", - content: { - text: "Hype is always real until it isn't. I'm taking out a fat short position here. Don't say I didn't warn you when this crashes 90% and you're left holding the flaming bags.", - }, - }, - ], - outcome: `\`\`\`json -[ - { - "recommender": "{{user2}}", - "ticker": "SAMOYED", - "contractAddress": "5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr", - "type": "buy", - "conviction": "medium", - "alreadyKnown": false - }, - { - "recommender": "{{user1}}", - "ticker": "SAMOYED", - "contractAddress": "5TQwHyZbedaH4Pcthj1Hxf5GqcigL6qWuB7YEsBtqvhr", - "type": "dont_buy", - "conviction": "high", - "alreadyKnown": false - } -] -\`\`\``, - }, - ], -}; diff --git a/packages/plugin-solana-agentkit/src/index.ts b/packages/plugin-solana-agentkit/src/index.ts index 210b12787b..3a7749a528 100644 --- a/packages/plugin-solana-agentkit/src/index.ts +++ b/packages/plugin-solana-agentkit/src/index.ts @@ -1,24 +1,12 @@ -export * from "./providers/token.ts"; -export * from "./providers/wallet.ts"; -export * from "./providers/trustScoreProvider.ts"; -export * from "./evaluators/trust.ts"; - import { Plugin } from "@elizaos/core"; -import transferToken from "./actions/transfer.ts"; -import { walletProvider } from "./providers/wallet.ts"; -import { trustScoreProvider } from "./providers/trustScoreProvider.ts"; -import { trustEvaluator } from "./evaluators/trust.ts"; -import { TokenProvider } from "./providers/token.ts"; -import { WalletProvider } from "./providers/wallet.ts"; - -export { TokenProvider, WalletProvider }; +import createToken from "./actions/createToken.ts"; export const solanaAgentkitPlguin: Plugin = { name: "solana", description: "Solana Plugin with solana agent kit for Eliza", - actions: [transferToken], - evaluators: [trustEvaluator], - providers: [walletProvider, trustScoreProvider], + actions: [createToken], + evaluators: [], + providers: [,], }; export default solanaAgentkitPlguin; diff --git a/packages/plugin-solana-agentkit/src/providers/orderBook.ts b/packages/plugin-solana-agentkit/src/providers/orderBook.ts deleted file mode 100644 index ac4577e012..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/orderBook.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; -interface Order { - userId: string; - ticker: string; - contractAddress: string; - timestamp: string; - buyAmount: number; - price: number; -} - -const orderBookProvider: Provider = { - get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => { - const userId = message.userId; - - // Read the order book from the JSON file - const orderBookPath = - runtime.getSetting("orderBookPath") ?? "solana/orderBook"; - - const orderBook: Order[] = []; - - const cachedOrderBook = - await runtime.cacheManager.get(orderBookPath); - - if (cachedOrderBook) { - orderBook.push(...cachedOrderBook); - } - - // Filter the orders for the current user - const userOrders = orderBook.filter((order) => order.userId === userId); - - let totalProfit = 0; - for (const order of userOrders) { - // Get the current price of the asset (replace with actual price fetching logic) - const currentPrice = 120; - - const priceDifference = currentPrice - order.price; - const orderProfit = priceDifference * order.buyAmount; - totalProfit += orderProfit; - } - - return `The user has made a total profit of $${totalProfit.toFixed(2)} for the agent based on their recorded buy orders.`; - }, -}; - -export { orderBookProvider }; diff --git a/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts b/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts deleted file mode 100644 index 670eeb74f3..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/simulationSellingService.ts +++ /dev/null @@ -1,501 +0,0 @@ -import { - TrustScoreDatabase, - TokenPerformance, - // TradePerformance, - TokenRecommendation, -} from "@elizaos/plugin-trustdb"; -import { Connection, PublicKey } from "@solana/web3.js"; -// Assuming TokenProvider and IAgentRuntime are available -import { TokenProvider } from "./token.ts"; -// import { settings } from "@elizaos/core"; -import { IAgentRuntime } from "@elizaos/core"; -import { WalletProvider } from "./wallet.ts"; -import * as amqp from "amqplib"; -import { ProcessedTokenData } from "../types/token.ts"; -import { getWalletKey } from "../keypairUtils.ts"; - -interface SellDetails { - sell_amount: number; - sell_recommender_id: string | null; -} - -export class SimulationSellingService { - private trustScoreDb: TrustScoreDatabase; - private walletProvider: WalletProvider; - private connection: Connection; - private baseMint: PublicKey; - private DECAY_RATE = 0.95; - private MAX_DECAY_DAYS = 30; - private backend: string; - private backendToken: string; - private amqpConnection: amqp.Connection; - private amqpChannel: amqp.Channel; - private sonarBe: string; - private sonarBeToken: string; - private runtime: IAgentRuntime; - - private runningProcesses: Set = new Set(); - - constructor(runtime: IAgentRuntime, trustScoreDb: TrustScoreDatabase) { - this.trustScoreDb = trustScoreDb; - - this.connection = new Connection(runtime.getSetting("RPC_URL")); - this.initializeWalletProvider(); - this.baseMint = new PublicKey( - runtime.getSetting("BASE_MINT") || - "So11111111111111111111111111111111111111112" - ); - this.backend = runtime.getSetting("BACKEND_URL"); - this.backendToken = runtime.getSetting("BACKEND_TOKEN"); - this.initializeRabbitMQ(runtime.getSetting("AMQP_URL")); - this.sonarBe = runtime.getSetting("SONAR_BE"); - this.sonarBeToken = runtime.getSetting("SONAR_BE_TOKEN"); - this.runtime = runtime; - } - /** - * Initializes the RabbitMQ connection and starts consuming messages. - * @param amqpUrl The RabbitMQ server URL. - */ - private async initializeRabbitMQ(amqpUrl: string) { - try { - this.amqpConnection = await amqp.connect(amqpUrl); - this.amqpChannel = await this.amqpConnection.createChannel(); - console.log("Connected to RabbitMQ"); - // Start consuming messages - this.consumeMessages(); - } catch (error) { - console.error("Failed to connect to RabbitMQ:", error); - } - } - - /** - * Sets up the consumer for the specified RabbitMQ queue. - */ - private async consumeMessages() { - const queue = "process_eliza_simulation"; - await this.amqpChannel.assertQueue(queue, { durable: true }); - this.amqpChannel.consume( - queue, - (msg) => { - if (msg !== null) { - const content = msg.content.toString(); - this.processMessage(content); - this.amqpChannel.ack(msg); - } - }, - { noAck: false } - ); - console.log(`Listening for messages on queue: ${queue}`); - } - - /** - * Processes incoming messages from RabbitMQ. - * @param message The message content as a string. - */ - private async processMessage(message: string) { - try { - const { tokenAddress, amount, sell_recommender_id } = - JSON.parse(message); - console.log( - `Received message for token ${tokenAddress} to sell ${amount}` - ); - - const decision: SellDecision = { - tokenPerformance: - await this.trustScoreDb.getTokenPerformance(tokenAddress), - amountToSell: amount, - sell_recommender_id: sell_recommender_id, - }; - - // Execute the sell - await this.executeSellDecision(decision); - - // Remove from running processes after completion - this.runningProcesses.delete(tokenAddress); - } catch (error) { - console.error("Error processing message:", error); - } - } - - /** - * Executes a single sell decision. - * @param decision The sell decision containing token performance and amount to sell. - */ - private async executeSellDecision(decision: SellDecision) { - const { tokenPerformance, amountToSell, sell_recommender_id } = - decision; - const tokenAddress = tokenPerformance.tokenAddress; - - try { - console.log( - `Executing sell for token ${tokenPerformance.symbol}: ${amountToSell}` - ); - - // Update the sell details - const sellDetails: SellDetails = { - sell_amount: amountToSell, - sell_recommender_id: sell_recommender_id, // Adjust if necessary - }; - const sellTimeStamp = new Date().toISOString(); - const tokenProvider = new TokenProvider( - tokenAddress, - this.walletProvider, - this.runtime.cacheManager - ); - - // Update sell details in the database - const sellDetailsData = await this.updateSellDetails( - tokenAddress, - sell_recommender_id, - sellTimeStamp, - sellDetails, - true, // isSimulation - tokenProvider - ); - - console.log("Sell order executed successfully", sellDetailsData); - - // check if balance is zero and remove token from running processes - const balance = this.trustScoreDb.getTokenBalance(tokenAddress); - if (balance === 0) { - this.runningProcesses.delete(tokenAddress); - } - // stop the process in the sonar backend - await this.stopProcessInTheSonarBackend(tokenAddress); - } catch (error) { - console.error( - `Error executing sell for token ${tokenAddress}:`, - error - ); - } - } - - /** - * Derives the public key based on the TEE (Trusted Execution Environment) mode and initializes the wallet provider. - * If TEE mode is enabled, derives a keypair using the DeriveKeyProvider with the wallet secret salt and agent ID. - * If TEE mode is disabled, uses the provided Solana public key or wallet public key from settings. - */ - private async initializeWalletProvider(): Promise { - const { publicKey } = await getWalletKey(this.runtime, false); - - this.walletProvider = new WalletProvider(this.connection, publicKey); - } - - public async startService() { - // starting the service - console.log("Starting SellingService..."); - await this.startListeners(); - } - - public async startListeners() { - // scanning recommendations and selling - console.log("Scanning for token performances..."); - const tokenPerformances = - await this.trustScoreDb.getAllTokenPerformancesWithBalance(); - - await this.processTokenPerformances(tokenPerformances); - } - - private processTokenPerformances(tokenPerformances: TokenPerformance[]) { - // To Do: logic when to sell and how much - console.log("Deciding when to sell and how much..."); - const runningProcesses = this.runningProcesses; - // remove running processes from tokenPerformances - tokenPerformances = tokenPerformances.filter( - (tp) => !runningProcesses.has(tp.tokenAddress) - ); - - // start the process in the sonar backend - tokenPerformances.forEach(async (tokenPerformance) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const tokenProvider = new TokenProvider( - tokenPerformance.tokenAddress, - this.walletProvider, - this.runtime.cacheManager - ); - // const shouldTrade = await tokenProvider.shouldTradeToken(); - // if (shouldTrade) { - const tokenRecommendations: TokenRecommendation[] = - this.trustScoreDb.getRecommendationsByToken( - tokenPerformance.tokenAddress - ); - const tokenRecommendation: TokenRecommendation = - tokenRecommendations[0]; - const balance = tokenPerformance.balance; - const sell_recommender_id = tokenRecommendation.recommenderId; - const tokenAddress = tokenPerformance.tokenAddress; - const process = await this.startProcessInTheSonarBackend( - tokenAddress, - balance, - true, - sell_recommender_id, - tokenPerformance.initialMarketCap - ); - if (process) { - this.runningProcesses.add(tokenAddress); - } - // } - }); - } - - public processTokenPerformance( - tokenAddress: string, - recommenderId: string - ) { - try { - const runningProcesses = this.runningProcesses; - // check if token is already being processed - if (runningProcesses.has(tokenAddress)) { - console.log(`Token ${tokenAddress} is already being processed`); - return; - } - const tokenPerformance = - this.trustScoreDb.getTokenPerformance(tokenAddress); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const tokenProvider = new TokenProvider( - tokenPerformance.tokenAddress, - this.walletProvider, - this.runtime.cacheManager - ); - const balance = tokenPerformance.balance; - const sell_recommender_id = recommenderId; - const process = this.startProcessInTheSonarBackend( - tokenAddress, - balance, - true, - sell_recommender_id, - tokenPerformance.initialMarketCap - ); - if (process) { - this.runningProcesses.add(tokenAddress); - } - } catch (error) { - console.error( - `Error getting token performance for token ${tokenAddress}:`, - error - ); - } - } - - private async startProcessInTheSonarBackend( - tokenAddress: string, - balance: number, - isSimulation: boolean, - sell_recommender_id: string, - initial_mc: number - ) { - try { - const message = JSON.stringify({ - tokenAddress, - balance, - isSimulation, - initial_mc, - sell_recommender_id, - }); - const response = await fetch( - `${this.sonarBe}/elizaos-sol/startProcess`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": `${this.sonarBeToken}`, - }, - body: message, - } - ); - - if (!response.ok) { - console.error( - `Failed to send message to process token ${tokenAddress}` - ); - return; - } - - const result = await response.json(); - console.log("Received response:", result); - console.log(`Sent message to process token ${tokenAddress}`); - - return result; - } catch (error) { - console.error( - `Error sending message to process token ${tokenAddress}:`, - error - ); - return null; - } - } - - private stopProcessInTheSonarBackend(tokenAddress: string) { - try { - return fetch(`${this.sonarBe}/elizaos-sol/stopProcess`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": `${this.sonarBeToken}`, - }, - body: JSON.stringify({ tokenAddress }), - }); - } catch (error) { - console.error( - `Error stopping process for token ${tokenAddress}:`, - error - ); - } - } - - async updateSellDetails( - tokenAddress: string, - recommenderId: string, - sellTimeStamp: string, - sellDetails: SellDetails, - isSimulation: boolean, - tokenProvider: TokenProvider - ) { - const recommender = - await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( - recommenderId - ); - const processedData: ProcessedTokenData = - await tokenProvider.getProcessedTokenData(); - const prices = await this.walletProvider.fetchPrices(null); - const solPrice = prices.solana.usd; - const sellSol = sellDetails.sell_amount / parseFloat(solPrice); - const sell_value_usd = - sellDetails.sell_amount * processedData.tradeData.price; - const trade = await this.trustScoreDb.getLatestTradePerformance( - tokenAddress, - recommender.id, - isSimulation - ); - const buyTimeStamp = trade.buy_timeStamp; - const marketCap = - processedData.dexScreenerData.pairs[0]?.marketCap || 0; - const liquidity = - processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0; - const sell_price = processedData.tradeData.price; - const profit_usd = sell_value_usd - trade.buy_value_usd; - const profit_percent = (profit_usd / trade.buy_value_usd) * 100; - - const market_cap_change = marketCap - trade.buy_market_cap; - const liquidity_change = liquidity - trade.buy_liquidity; - - const isRapidDump = await this.isRapidDump(tokenAddress, tokenProvider); - - const sellDetailsData = { - sell_price: sell_price, - sell_timeStamp: sellTimeStamp, - sell_amount: sellDetails.sell_amount, - received_sol: sellSol, - sell_value_usd: sell_value_usd, - profit_usd: profit_usd, - profit_percent: profit_percent, - sell_market_cap: marketCap, - market_cap_change: market_cap_change, - sell_liquidity: liquidity, - liquidity_change: liquidity_change, - rapidDump: isRapidDump, - sell_recommender_id: sellDetails.sell_recommender_id || null, - }; - this.trustScoreDb.updateTradePerformanceOnSell( - tokenAddress, - recommender.id, - buyTimeStamp, - sellDetailsData, - isSimulation - ); - - // If the trade is a simulation update the balance - const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); - const tokenBalance = oldBalance - sellDetails.sell_amount; - this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); - // generate some random hash for simulations - const hash = Math.random().toString(36).substring(7); - const transaction = { - tokenAddress: tokenAddress, - type: "sell" as "buy" | "sell", - transactionHash: hash, - amount: sellDetails.sell_amount, - price: processedData.tradeData.price, - isSimulation: true, - timestamp: new Date().toISOString(), - }; - this.trustScoreDb.addTransaction(transaction); - this.updateTradeInBe( - tokenAddress, - recommender.id, - recommender.telegramId, - sellDetailsData, - tokenBalance - ); - - return sellDetailsData; - } - async isRapidDump( - tokenAddress: string, - tokenProvider: TokenProvider - ): Promise { - const processedData: ProcessedTokenData = - await tokenProvider.getProcessedTokenData(); - console.log(`Fetched processed token data for token: ${tokenAddress}`); - - return processedData.tradeData.trade_24h_change_percent < -50; - } - - async delay(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - - async updateTradeInBe( - tokenAddress: string, - recommenderId: string, - username: string, - data: SellDetails, - balanceLeft: number, - retries = 3, - delayMs = 2000 - ) { - for (let attempt = 1; attempt <= retries; attempt++) { - try { - await fetch( - `${this.backend}/api/updaters/updateTradePerformance`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.backendToken}`, - }, - body: JSON.stringify({ - tokenAddress: tokenAddress, - tradeData: data, - recommenderId: recommenderId, - username: username, - isSimulation: true, - balanceLeft: balanceLeft, - }), - } - ); - // If the request is successful, exit the loop - return; - } catch (error) { - console.error( - `Attempt ${attempt} failed: Error creating trade in backend`, - error - ); - if (attempt < retries) { - console.log(`Retrying in ${delayMs} ms...`); - await this.delay(delayMs); // Wait for the specified delay before retrying - } else { - console.error("All attempts failed."); - } - } - } - } -} - -// SellDecision interface -interface SellDecision { - tokenPerformance: TokenPerformance; - amountToSell: number; - sell_recommender_id: string | null; -} diff --git a/packages/plugin-solana-agentkit/src/providers/token.ts b/packages/plugin-solana-agentkit/src/providers/token.ts deleted file mode 100644 index d8e885915a..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/token.ts +++ /dev/null @@ -1,1124 +0,0 @@ -import { ICacheManager, settings } from "@elizaos/core"; -import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; -import { - DexScreenerData, - DexScreenerPair, - HolderData, - ProcessedTokenData, - TokenSecurityData, - TokenTradeData, - CalculatedBuyAmounts, - Prices, - TokenCodex, -} from "../types/token.ts"; -import NodeCache from "node-cache"; -import * as path from "path"; -import { toBN } from "../bignumber.ts"; -import { WalletProvider, Item } from "./wallet.ts"; -import { Connection } from "@solana/web3.js"; -import { getWalletKey } from "../keypairUtils.ts"; - -const PROVIDER_CONFIG = { - BIRDEYE_API: "https://public-api.birdeye.so", - MAX_RETRIES: 3, - RETRY_DELAY: 2000, - DEFAULT_RPC: "https://api.mainnet-beta.solana.com", - TOKEN_ADDRESSES: { - SOL: "So11111111111111111111111111111111111111112", - BTC: "qfnqNqs3nCAHjnyCgLRDbBtq4p2MtHZxw8YjSyYhPoL", - ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", - Example: "2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh", - }, - TOKEN_SECURITY_ENDPOINT: "/defi/token_security?address=", - TOKEN_TRADE_DATA_ENDPOINT: "/defi/v3/token/trade-data/single?address=", - DEX_SCREENER_API: "https://api.dexscreener.com/latest/dex/tokens/", - MAIN_WALLET: "", -}; - -export class TokenProvider { - private cache: NodeCache; - private cacheKey: string = "solana/tokens"; - private NETWORK_ID = 1399811149; - private GRAPHQL_ENDPOINT = "https://graph.codex.io/graphql"; - - constructor( - // private connection: Connection, - private tokenAddress: string, - private walletProvider: WalletProvider, - private cacheManager: ICacheManager - ) { - this.cache = new NodeCache({ stdTTL: 300 }); // 5 minutes cache - } - - private async readFromCache(key: string): Promise { - const cached = await this.cacheManager.get( - path.join(this.cacheKey, key) - ); - return cached; - } - - private async writeToCache(key: string, data: T): Promise { - await this.cacheManager.set(path.join(this.cacheKey, key), data, { - expires: Date.now() + 5 * 60 * 1000, - }); - } - - private async getCachedData(key: string): Promise { - // Check in-memory cache first - const cachedData = this.cache.get(key); - if (cachedData) { - return cachedData; - } - - // Check file-based cache - const fileCachedData = await this.readFromCache(key); - if (fileCachedData) { - // Populate in-memory cache - this.cache.set(key, fileCachedData); - return fileCachedData; - } - - return null; - } - - private async setCachedData(cacheKey: string, data: T): Promise { - // Set in-memory cache - this.cache.set(cacheKey, data); - - // Write to file-based cache - await this.writeToCache(cacheKey, data); - } - - private async fetchWithRetry( - url: string, - options: RequestInit = {} - ): Promise { - let lastError: Error; - - for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { - try { - const response = await fetch(url, { - ...options, - headers: { - Accept: "application/json", - "x-chain": "solana", - "X-API-KEY": settings.BIRDEYE_API_KEY || "", - ...options.headers, - }, - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error( - `HTTP error! status: ${response.status}, message: ${errorText}` - ); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error(`Attempt ${i + 1} failed:`, error); - lastError = error as Error; - if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { - const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); - console.log(`Waiting ${delay}ms before retrying...`); - await new Promise((resolve) => setTimeout(resolve, delay)); - continue; - } - } - } - - console.error( - "All attempts failed. Throwing the last error:", - lastError - ); - throw lastError; - } - - async getTokensInWallet(runtime: IAgentRuntime): Promise { - const walletInfo = - await this.walletProvider.fetchPortfolioValue(runtime); - const items = walletInfo.items; - return items; - } - - // check if the token symbol is in the wallet - async getTokenFromWallet(runtime: IAgentRuntime, tokenSymbol: string) { - try { - const items = await this.getTokensInWallet(runtime); - const token = items.find((item) => item.symbol === tokenSymbol); - - if (token) { - return token.address; - } else { - return null; - } - } catch (error) { - console.error("Error checking token in wallet:", error); - return null; - } - } - - async fetchTokenCodex(): Promise { - try { - const cacheKey = `token_${this.tokenAddress}`; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log( - `Returning cached token data for ${this.tokenAddress}.` - ); - return cachedData; - } - const query = ` - query Token($address: String!, $networkId: Int!) { - token(input: { address: $address, networkId: $networkId }) { - id - address - cmcId - decimals - name - symbol - totalSupply - isScam - info { - circulatingSupply - imageThumbUrl - } - explorerData { - blueCheckmark - description - tokenType - } - } - } - `; - - const variables = { - address: this.tokenAddress, - networkId: this.NETWORK_ID, // Replace with your network ID - }; - - const response = await fetch(this.GRAPHQL_ENDPOINT, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: settings.CODEX_API_KEY, - }, - body: JSON.stringify({ - query, - variables, - }), - }).then((res) => res.json()); - - const token = response.data?.data?.token; - - if (!token) { - throw new Error(`No data returned for token ${tokenAddress}`); - } - - this.setCachedData(cacheKey, token); - - return { - id: token.id, - address: token.address, - cmcId: token.cmcId, - decimals: token.decimals, - name: token.name, - symbol: token.symbol, - totalSupply: token.totalSupply, - circulatingSupply: token.info?.circulatingSupply, - imageThumbUrl: token.info?.imageThumbUrl, - blueCheckmark: token.explorerData?.blueCheckmark, - isScam: token.isScam ? true : false, - }; - } catch (error) { - console.error( - "Error fetching token data from Codex:", - error.message - ); - return {} as TokenCodex; - } - } - - async fetchPrices(): Promise { - try { - const cacheKey = "prices"; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log("Returning cached prices."); - return cachedData; - } - const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; - const tokens = [SOL, BTC, ETH]; - const prices: Prices = { - solana: { usd: "0" }, - bitcoin: { usd: "0" }, - ethereum: { usd: "0" }, - }; - - for (const token of tokens) { - const response = await this.fetchWithRetry( - `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, - { - headers: { - "x-chain": "solana", - }, - } - ); - - if (response?.data?.value) { - const price = response.data.value.toString(); - prices[ - token === SOL - ? "solana" - : token === BTC - ? "bitcoin" - : "ethereum" - ].usd = price; - } else { - console.warn(`No price data available for token: ${token}`); - } - } - this.setCachedData(cacheKey, prices); - return prices; - } catch (error) { - console.error("Error fetching prices:", error); - throw error; - } - } - async calculateBuyAmounts(): Promise { - const dexScreenerData = await this.fetchDexScreenerData(); - const prices = await this.fetchPrices(); - const solPrice = toBN(prices.solana.usd); - - if (!dexScreenerData || dexScreenerData.pairs.length === 0) { - return { none: 0, low: 0, medium: 0, high: 0 }; - } - - // Get the first pair - const pair = dexScreenerData.pairs[0]; - const { liquidity, marketCap } = pair; - if (!liquidity || !marketCap) { - return { none: 0, low: 0, medium: 0, high: 0 }; - } - - if (liquidity.usd === 0) { - return { none: 0, low: 0, medium: 0, high: 0 }; - } - if (marketCap < 100000) { - return { none: 0, low: 0, medium: 0, high: 0 }; - } - - // impact percentages based on liquidity - const impactPercentages = { - LOW: 0.01, // 1% of liquidity - MEDIUM: 0.05, // 5% of liquidity - HIGH: 0.1, // 10% of liquidity - }; - - // Calculate buy amounts in USD - const lowBuyAmountUSD = liquidity.usd * impactPercentages.LOW; - const mediumBuyAmountUSD = liquidity.usd * impactPercentages.MEDIUM; - const highBuyAmountUSD = liquidity.usd * impactPercentages.HIGH; - - // Convert each buy amount to SOL - const lowBuyAmountSOL = toBN(lowBuyAmountUSD).div(solPrice).toNumber(); - const mediumBuyAmountSOL = toBN(mediumBuyAmountUSD) - .div(solPrice) - .toNumber(); - const highBuyAmountSOL = toBN(highBuyAmountUSD) - .div(solPrice) - .toNumber(); - - return { - none: 0, - low: lowBuyAmountSOL, - medium: mediumBuyAmountSOL, - high: highBuyAmountSOL, - }; - } - - async fetchTokenSecurity(): Promise { - const cacheKey = `tokenSecurity_${this.tokenAddress}`; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log( - `Returning cached token security data for ${this.tokenAddress}.` - ); - return cachedData; - } - const url = `${PROVIDER_CONFIG.BIRDEYE_API}${PROVIDER_CONFIG.TOKEN_SECURITY_ENDPOINT}${this.tokenAddress}`; - const data = await this.fetchWithRetry(url); - - if (!data?.success || !data?.data) { - throw new Error("No token security data available"); - } - - const security: TokenSecurityData = { - ownerBalance: data.data.ownerBalance, - creatorBalance: data.data.creatorBalance, - ownerPercentage: data.data.ownerPercentage, - creatorPercentage: data.data.creatorPercentage, - top10HolderBalance: data.data.top10HolderBalance, - top10HolderPercent: data.data.top10HolderPercent, - }; - this.setCachedData(cacheKey, security); - console.log(`Token security data cached for ${this.tokenAddress}.`); - - return security; - } - - async fetchTokenTradeData(): Promise { - const cacheKey = `tokenTradeData_${this.tokenAddress}`; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log( - `Returning cached token trade data for ${this.tokenAddress}.` - ); - return cachedData; - } - - const url = `${PROVIDER_CONFIG.BIRDEYE_API}${PROVIDER_CONFIG.TOKEN_TRADE_DATA_ENDPOINT}${this.tokenAddress}`; - const options = { - method: "GET", - headers: { - accept: "application/json", - "X-API-KEY": settings.BIRDEYE_API_KEY || "", - }, - }; - - const data = await fetch(url, options) - .then((res) => res.json()) - .catch((err) => console.error(err)); - - if (!data?.success || !data?.data) { - throw new Error("No token trade data available"); - } - - const tradeData: TokenTradeData = { - address: data.data.address, - holder: data.data.holder, - market: data.data.market, - last_trade_unix_time: data.data.last_trade_unix_time, - last_trade_human_time: data.data.last_trade_human_time, - price: data.data.price, - history_30m_price: data.data.history_30m_price, - price_change_30m_percent: data.data.price_change_30m_percent, - history_1h_price: data.data.history_1h_price, - price_change_1h_percent: data.data.price_change_1h_percent, - history_2h_price: data.data.history_2h_price, - price_change_2h_percent: data.data.price_change_2h_percent, - history_4h_price: data.data.history_4h_price, - price_change_4h_percent: data.data.price_change_4h_percent, - history_6h_price: data.data.history_6h_price, - price_change_6h_percent: data.data.price_change_6h_percent, - history_8h_price: data.data.history_8h_price, - price_change_8h_percent: data.data.price_change_8h_percent, - history_12h_price: data.data.history_12h_price, - price_change_12h_percent: data.data.price_change_12h_percent, - history_24h_price: data.data.history_24h_price, - price_change_24h_percent: data.data.price_change_24h_percent, - unique_wallet_30m: data.data.unique_wallet_30m, - unique_wallet_history_30m: data.data.unique_wallet_history_30m, - unique_wallet_30m_change_percent: - data.data.unique_wallet_30m_change_percent, - unique_wallet_1h: data.data.unique_wallet_1h, - unique_wallet_history_1h: data.data.unique_wallet_history_1h, - unique_wallet_1h_change_percent: - data.data.unique_wallet_1h_change_percent, - unique_wallet_2h: data.data.unique_wallet_2h, - unique_wallet_history_2h: data.data.unique_wallet_history_2h, - unique_wallet_2h_change_percent: - data.data.unique_wallet_2h_change_percent, - unique_wallet_4h: data.data.unique_wallet_4h, - unique_wallet_history_4h: data.data.unique_wallet_history_4h, - unique_wallet_4h_change_percent: - data.data.unique_wallet_4h_change_percent, - unique_wallet_8h: data.data.unique_wallet_8h, - unique_wallet_history_8h: data.data.unique_wallet_history_8h, - unique_wallet_8h_change_percent: - data.data.unique_wallet_8h_change_percent, - unique_wallet_24h: data.data.unique_wallet_24h, - unique_wallet_history_24h: data.data.unique_wallet_history_24h, - unique_wallet_24h_change_percent: - data.data.unique_wallet_24h_change_percent, - trade_30m: data.data.trade_30m, - trade_history_30m: data.data.trade_history_30m, - trade_30m_change_percent: data.data.trade_30m_change_percent, - sell_30m: data.data.sell_30m, - sell_history_30m: data.data.sell_history_30m, - sell_30m_change_percent: data.data.sell_30m_change_percent, - buy_30m: data.data.buy_30m, - buy_history_30m: data.data.buy_history_30m, - buy_30m_change_percent: data.data.buy_30m_change_percent, - volume_30m: data.data.volume_30m, - volume_30m_usd: data.data.volume_30m_usd, - volume_history_30m: data.data.volume_history_30m, - volume_history_30m_usd: data.data.volume_history_30m_usd, - volume_30m_change_percent: data.data.volume_30m_change_percent, - volume_buy_30m: data.data.volume_buy_30m, - volume_buy_30m_usd: data.data.volume_buy_30m_usd, - volume_buy_history_30m: data.data.volume_buy_history_30m, - volume_buy_history_30m_usd: data.data.volume_buy_history_30m_usd, - volume_buy_30m_change_percent: - data.data.volume_buy_30m_change_percent, - volume_sell_30m: data.data.volume_sell_30m, - volume_sell_30m_usd: data.data.volume_sell_30m_usd, - volume_sell_history_30m: data.data.volume_sell_history_30m, - volume_sell_history_30m_usd: data.data.volume_sell_history_30m_usd, - volume_sell_30m_change_percent: - data.data.volume_sell_30m_change_percent, - trade_1h: data.data.trade_1h, - trade_history_1h: data.data.trade_history_1h, - trade_1h_change_percent: data.data.trade_1h_change_percent, - sell_1h: data.data.sell_1h, - sell_history_1h: data.data.sell_history_1h, - sell_1h_change_percent: data.data.sell_1h_change_percent, - buy_1h: data.data.buy_1h, - buy_history_1h: data.data.buy_history_1h, - buy_1h_change_percent: data.data.buy_1h_change_percent, - volume_1h: data.data.volume_1h, - volume_1h_usd: data.data.volume_1h_usd, - volume_history_1h: data.data.volume_history_1h, - volume_history_1h_usd: data.data.volume_history_1h_usd, - volume_1h_change_percent: data.data.volume_1h_change_percent, - volume_buy_1h: data.data.volume_buy_1h, - volume_buy_1h_usd: data.data.volume_buy_1h_usd, - volume_buy_history_1h: data.data.volume_buy_history_1h, - volume_buy_history_1h_usd: data.data.volume_buy_history_1h_usd, - volume_buy_1h_change_percent: - data.data.volume_buy_1h_change_percent, - volume_sell_1h: data.data.volume_sell_1h, - volume_sell_1h_usd: data.data.volume_sell_1h_usd, - volume_sell_history_1h: data.data.volume_sell_history_1h, - volume_sell_history_1h_usd: data.data.volume_sell_history_1h_usd, - volume_sell_1h_change_percent: - data.data.volume_sell_1h_change_percent, - trade_2h: data.data.trade_2h, - trade_history_2h: data.data.trade_history_2h, - trade_2h_change_percent: data.data.trade_2h_change_percent, - sell_2h: data.data.sell_2h, - sell_history_2h: data.data.sell_history_2h, - sell_2h_change_percent: data.data.sell_2h_change_percent, - buy_2h: data.data.buy_2h, - buy_history_2h: data.data.buy_history_2h, - buy_2h_change_percent: data.data.buy_2h_change_percent, - volume_2h: data.data.volume_2h, - volume_2h_usd: data.data.volume_2h_usd, - volume_history_2h: data.data.volume_history_2h, - volume_history_2h_usd: data.data.volume_history_2h_usd, - volume_2h_change_percent: data.data.volume_2h_change_percent, - volume_buy_2h: data.data.volume_buy_2h, - volume_buy_2h_usd: data.data.volume_buy_2h_usd, - volume_buy_history_2h: data.data.volume_buy_history_2h, - volume_buy_history_2h_usd: data.data.volume_buy_history_2h_usd, - volume_buy_2h_change_percent: - data.data.volume_buy_2h_change_percent, - volume_sell_2h: data.data.volume_sell_2h, - volume_sell_2h_usd: data.data.volume_sell_2h_usd, - volume_sell_history_2h: data.data.volume_sell_history_2h, - volume_sell_history_2h_usd: data.data.volume_sell_history_2h_usd, - volume_sell_2h_change_percent: - data.data.volume_sell_2h_change_percent, - trade_4h: data.data.trade_4h, - trade_history_4h: data.data.trade_history_4h, - trade_4h_change_percent: data.data.trade_4h_change_percent, - sell_4h: data.data.sell_4h, - sell_history_4h: data.data.sell_history_4h, - sell_4h_change_percent: data.data.sell_4h_change_percent, - buy_4h: data.data.buy_4h, - buy_history_4h: data.data.buy_history_4h, - buy_4h_change_percent: data.data.buy_4h_change_percent, - volume_4h: data.data.volume_4h, - volume_4h_usd: data.data.volume_4h_usd, - volume_history_4h: data.data.volume_history_4h, - volume_history_4h_usd: data.data.volume_history_4h_usd, - volume_4h_change_percent: data.data.volume_4h_change_percent, - volume_buy_4h: data.data.volume_buy_4h, - volume_buy_4h_usd: data.data.volume_buy_4h_usd, - volume_buy_history_4h: data.data.volume_buy_history_4h, - volume_buy_history_4h_usd: data.data.volume_buy_history_4h_usd, - volume_buy_4h_change_percent: - data.data.volume_buy_4h_change_percent, - volume_sell_4h: data.data.volume_sell_4h, - volume_sell_4h_usd: data.data.volume_sell_4h_usd, - volume_sell_history_4h: data.data.volume_sell_history_4h, - volume_sell_history_4h_usd: data.data.volume_sell_history_4h_usd, - volume_sell_4h_change_percent: - data.data.volume_sell_4h_change_percent, - trade_8h: data.data.trade_8h, - trade_history_8h: data.data.trade_history_8h, - trade_8h_change_percent: data.data.trade_8h_change_percent, - sell_8h: data.data.sell_8h, - sell_history_8h: data.data.sell_history_8h, - sell_8h_change_percent: data.data.sell_8h_change_percent, - buy_8h: data.data.buy_8h, - buy_history_8h: data.data.buy_history_8h, - buy_8h_change_percent: data.data.buy_8h_change_percent, - volume_8h: data.data.volume_8h, - volume_8h_usd: data.data.volume_8h_usd, - volume_history_8h: data.data.volume_history_8h, - volume_history_8h_usd: data.data.volume_history_8h_usd, - volume_8h_change_percent: data.data.volume_8h_change_percent, - volume_buy_8h: data.data.volume_buy_8h, - volume_buy_8h_usd: data.data.volume_buy_8h_usd, - volume_buy_history_8h: data.data.volume_buy_history_8h, - volume_buy_history_8h_usd: data.data.volume_buy_history_8h_usd, - volume_buy_8h_change_percent: - data.data.volume_buy_8h_change_percent, - volume_sell_8h: data.data.volume_sell_8h, - volume_sell_8h_usd: data.data.volume_sell_8h_usd, - volume_sell_history_8h: data.data.volume_sell_history_8h, - volume_sell_history_8h_usd: data.data.volume_sell_history_8h_usd, - volume_sell_8h_change_percent: - data.data.volume_sell_8h_change_percent, - trade_24h: data.data.trade_24h, - trade_history_24h: data.data.trade_history_24h, - trade_24h_change_percent: data.data.trade_24h_change_percent, - sell_24h: data.data.sell_24h, - sell_history_24h: data.data.sell_history_24h, - sell_24h_change_percent: data.data.sell_24h_change_percent, - buy_24h: data.data.buy_24h, - buy_history_24h: data.data.buy_history_24h, - buy_24h_change_percent: data.data.buy_24h_change_percent, - volume_24h: data.data.volume_24h, - volume_24h_usd: data.data.volume_24h_usd, - volume_history_24h: data.data.volume_history_24h, - volume_history_24h_usd: data.data.volume_history_24h_usd, - volume_24h_change_percent: data.data.volume_24h_change_percent, - volume_buy_24h: data.data.volume_buy_24h, - volume_buy_24h_usd: data.data.volume_buy_24h_usd, - volume_buy_history_24h: data.data.volume_buy_history_24h, - volume_buy_history_24h_usd: data.data.volume_buy_history_24h_usd, - volume_buy_24h_change_percent: - data.data.volume_buy_24h_change_percent, - volume_sell_24h: data.data.volume_sell_24h, - volume_sell_24h_usd: data.data.volume_sell_24h_usd, - volume_sell_history_24h: data.data.volume_sell_history_24h, - volume_sell_history_24h_usd: data.data.volume_sell_history_24h_usd, - volume_sell_24h_change_percent: - data.data.volume_sell_24h_change_percent, - }; - this.setCachedData(cacheKey, tradeData); - return tradeData; - } - - async fetchDexScreenerData(): Promise { - const cacheKey = `dexScreenerData_${this.tokenAddress}`; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log("Returning cached DexScreener data."); - return cachedData; - } - - const url = `https://api.dexscreener.com/latest/dex/search?q=${this.tokenAddress}`; - try { - console.log( - `Fetching DexScreener data for token: ${this.tokenAddress}` - ); - const data = await fetch(url) - .then((res) => res.json()) - .catch((err) => { - console.error(err); - }); - - if (!data || !data.pairs) { - throw new Error("No DexScreener data available"); - } - - const dexData: DexScreenerData = { - schemaVersion: data.schemaVersion, - pairs: data.pairs, - }; - - // Cache the result - this.setCachedData(cacheKey, dexData); - - return dexData; - } catch (error) { - console.error(`Error fetching DexScreener data:`, error); - return { - schemaVersion: "1.0.0", - pairs: [], - }; - } - } - - async searchDexScreenerData( - symbol: string - ): Promise { - const cacheKey = `dexScreenerData_search_${symbol}`; - const cachedData = await this.getCachedData(cacheKey); - if (cachedData) { - console.log("Returning cached search DexScreener data."); - return this.getHighestLiquidityPair(cachedData); - } - - const url = `https://api.dexscreener.com/latest/dex/search?q=${symbol}`; - try { - console.log(`Fetching DexScreener data for symbol: ${symbol}`); - const data = await fetch(url) - .then((res) => res.json()) - .catch((err) => { - console.error(err); - return null; - }); - - if (!data || !data.pairs || data.pairs.length === 0) { - throw new Error("No DexScreener data available"); - } - - const dexData: DexScreenerData = { - schemaVersion: data.schemaVersion, - pairs: data.pairs, - }; - - // Cache the result - this.setCachedData(cacheKey, dexData); - - // Return the pair with the highest liquidity and market cap - return this.getHighestLiquidityPair(dexData); - } catch (error) { - console.error(`Error fetching DexScreener data:`, error); - return null; - } - } - getHighestLiquidityPair(dexData: DexScreenerData): DexScreenerPair | null { - if (dexData.pairs.length === 0) { - return null; - } - - // Sort pairs by both liquidity and market cap to get the highest one - return dexData.pairs.sort((a, b) => { - const liquidityDiff = b.liquidity.usd - a.liquidity.usd; - if (liquidityDiff !== 0) { - return liquidityDiff; // Higher liquidity comes first - } - return b.marketCap - a.marketCap; // If liquidity is equal, higher market cap comes first - })[0]; - } - - async analyzeHolderDistribution( - tradeData: TokenTradeData - ): Promise { - // Define the time intervals to consider (e.g., 30m, 1h, 2h) - const intervals = [ - { - period: "30m", - change: tradeData.unique_wallet_30m_change_percent, - }, - { period: "1h", change: tradeData.unique_wallet_1h_change_percent }, - { period: "2h", change: tradeData.unique_wallet_2h_change_percent }, - { period: "4h", change: tradeData.unique_wallet_4h_change_percent }, - { period: "8h", change: tradeData.unique_wallet_8h_change_percent }, - { - period: "24h", - change: tradeData.unique_wallet_24h_change_percent, - }, - ]; - - // Calculate the average change percentage - const validChanges = intervals - .map((interval) => interval.change) - .filter( - (change) => change !== null && change !== undefined - ) as number[]; - - if (validChanges.length === 0) { - return "stable"; - } - - const averageChange = - validChanges.reduce((acc, curr) => acc + curr, 0) / - validChanges.length; - - const increaseThreshold = 10; // e.g., average change > 10% - const decreaseThreshold = -10; // e.g., average change < -10% - - if (averageChange > increaseThreshold) { - return "increasing"; - } else if (averageChange < decreaseThreshold) { - return "decreasing"; - } else { - return "stable"; - } - } - - async fetchHolderList(): Promise { - const cacheKey = `holderList_${this.tokenAddress}`; - const cachedData = this.getCachedData(cacheKey); - if (cachedData) { - console.log("Returning cached holder list."); - return cachedData; - } - - const allHoldersMap = new Map(); - let page = 1; - const limit = 1000; - let cursor; - //HELIOUS_API_KEY needs to be added - const url = `https://mainnet.helius-rpc.com/?api-key=${settings.HELIUS_API_KEY || ""}`; - console.log({ url }); - - try { - while (true) { - const params = { - limit: limit, - displayOptions: {}, - mint: this.tokenAddress, - cursor: cursor, - }; - if (cursor != undefined) { - params.cursor = cursor; - } - console.log(`Fetching holders - Page ${page}`); - if (page > 2) { - break; - } - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - jsonrpc: "2.0", - id: "helius-test", - method: "getTokenAccounts", - params: params, - }), - }); - - const data = await response.json(); - - if ( - !data || - !data.result || - !data.result.token_accounts || - data.result.token_accounts.length === 0 - ) { - console.log( - `No more holders found. Total pages fetched: ${page - 1}` - ); - break; - } - - console.log( - `Processing ${data.result.token_accounts.length} holders from page ${page}` - ); - - data.result.token_accounts.forEach((account: any) => { - const owner = account.owner; - const balance = parseFloat(account.amount); - - if (allHoldersMap.has(owner)) { - allHoldersMap.set( - owner, - allHoldersMap.get(owner)! + balance - ); - } else { - allHoldersMap.set(owner, balance); - } - }); - cursor = data.result.cursor; - page++; - } - - const holders: HolderData[] = Array.from( - allHoldersMap.entries() - ).map(([address, balance]) => ({ - address, - balance: balance.toString(), - })); - - console.log(`Total unique holders fetched: ${holders.length}`); - - // Cache the result - this.setCachedData(cacheKey, holders); - - return holders; - } catch (error) { - console.error("Error fetching holder list from Helius:", error); - throw new Error("Failed to fetch holder list from Helius."); - } - } - - async filterHighValueHolders( - tradeData: TokenTradeData - ): Promise> { - const holdersData = await this.fetchHolderList(); - - const tokenPriceUsd = toBN(tradeData.price); - - const highValueHolders = holdersData - .filter((holder) => { - const balanceUsd = toBN(holder.balance).multipliedBy( - tokenPriceUsd - ); - return balanceUsd.isGreaterThan(5); - }) - .map((holder) => ({ - holderAddress: holder.address, - balanceUsd: toBN(holder.balance) - .multipliedBy(tokenPriceUsd) - .toFixed(2), - })); - - return highValueHolders; - } - - async checkRecentTrades(tradeData: TokenTradeData): Promise { - return toBN(tradeData.volume_24h_usd).isGreaterThan(0); - } - - async countHighSupplyHolders( - securityData: TokenSecurityData - ): Promise { - try { - const ownerBalance = toBN(securityData.ownerBalance); - const totalSupply = ownerBalance.plus(securityData.creatorBalance); - - const highSupplyHolders = await this.fetchHolderList(); - const highSupplyHoldersCount = highSupplyHolders.filter( - (holder) => { - const balance = toBN(holder.balance); - return balance.dividedBy(totalSupply).isGreaterThan(0.02); - } - ).length; - return highSupplyHoldersCount; - } catch (error) { - console.error("Error counting high supply holders:", error); - return 0; - } - } - - async getProcessedTokenData(): Promise { - try { - console.log( - `Fetching security data for token: ${this.tokenAddress}` - ); - const security = await this.fetchTokenSecurity(); - - const tokenCodex = await this.fetchTokenCodex(); - - console.log(`Fetching trade data for token: ${this.tokenAddress}`); - const tradeData = await this.fetchTokenTradeData(); - - console.log( - `Fetching DexScreener data for token: ${this.tokenAddress}` - ); - const dexData = await this.fetchDexScreenerData(); - - console.log( - `Analyzing holder distribution for token: ${this.tokenAddress}` - ); - const holderDistributionTrend = - await this.analyzeHolderDistribution(tradeData); - - console.log( - `Filtering high-value holders for token: ${this.tokenAddress}` - ); - const highValueHolders = - await this.filterHighValueHolders(tradeData); - - console.log( - `Checking recent trades for token: ${this.tokenAddress}` - ); - const recentTrades = await this.checkRecentTrades(tradeData); - - console.log( - `Counting high-supply holders for token: ${this.tokenAddress}` - ); - const highSupplyHoldersCount = - await this.countHighSupplyHolders(security); - - console.log( - `Determining DexScreener listing status for token: ${this.tokenAddress}` - ); - const isDexScreenerListed = dexData.pairs.length > 0; - const isDexScreenerPaid = dexData.pairs.some( - (pair) => pair.boosts && pair.boosts.active > 0 - ); - - const processedData: ProcessedTokenData = { - security, - tradeData, - holderDistributionTrend, - highValueHolders, - recentTrades, - highSupplyHoldersCount, - dexScreenerData: dexData, - isDexScreenerListed, - isDexScreenerPaid, - tokenCodex, - }; - - // console.log("Processed token data:", processedData); - return processedData; - } catch (error) { - console.error("Error processing token data:", error); - throw error; - } - } - - async shouldTradeToken(): Promise { - try { - const tokenData = await this.getProcessedTokenData(); - const { tradeData, security, dexScreenerData } = tokenData; - const { ownerBalance, creatorBalance } = security; - const { liquidity, marketCap } = dexScreenerData.pairs[0]; - const liquidityUsd = toBN(liquidity.usd); - const marketCapUsd = toBN(marketCap); - const totalSupply = toBN(ownerBalance).plus(creatorBalance); - const _ownerPercentage = toBN(ownerBalance).dividedBy(totalSupply); - const _creatorPercentage = - toBN(creatorBalance).dividedBy(totalSupply); - const top10HolderPercent = toBN(tradeData.volume_24h_usd).dividedBy( - totalSupply - ); - const priceChange24hPercent = toBN( - tradeData.price_change_24h_percent - ); - const priceChange12hPercent = toBN( - tradeData.price_change_12h_percent - ); - const uniqueWallet24h = tradeData.unique_wallet_24h; - const volume24hUsd = toBN(tradeData.volume_24h_usd); - const volume24hUsdThreshold = 1000; - const priceChange24hPercentThreshold = 10; - const priceChange12hPercentThreshold = 5; - const top10HolderPercentThreshold = 0.05; - const uniqueWallet24hThreshold = 100; - const isTop10Holder = top10HolderPercent.gte( - top10HolderPercentThreshold - ); - const isVolume24h = volume24hUsd.gte(volume24hUsdThreshold); - const isPriceChange24h = priceChange24hPercent.gte( - priceChange24hPercentThreshold - ); - const isPriceChange12h = priceChange12hPercent.gte( - priceChange12hPercentThreshold - ); - const isUniqueWallet24h = - uniqueWallet24h >= uniqueWallet24hThreshold; - const isLiquidityTooLow = liquidityUsd.lt(1000); - const isMarketCapTooLow = marketCapUsd.lt(100000); - return ( - isTop10Holder || - isVolume24h || - isPriceChange24h || - isPriceChange12h || - isUniqueWallet24h || - isLiquidityTooLow || - isMarketCapTooLow - ); - } catch (error) { - console.error("Error processing token data:", error); - throw error; - } - } - - formatTokenData(data: ProcessedTokenData): string { - let output = `**Token Security and Trade Report**\n`; - output += `Token Address: ${this.tokenAddress}\n\n`; - - // Security Data - output += `**Ownership Distribution:**\n`; - output += `- Owner Balance: ${data.security.ownerBalance}\n`; - output += `- Creator Balance: ${data.security.creatorBalance}\n`; - output += `- Owner Percentage: ${data.security.ownerPercentage}%\n`; - output += `- Creator Percentage: ${data.security.creatorPercentage}%\n`; - output += `- Top 10 Holders Balance: ${data.security.top10HolderBalance}\n`; - output += `- Top 10 Holders Percentage: ${data.security.top10HolderPercent}%\n\n`; - - // Trade Data - output += `**Trade Data:**\n`; - output += `- Holders: ${data.tradeData.holder}\n`; - output += `- Unique Wallets (24h): ${data.tradeData.unique_wallet_24h}\n`; - output += `- Price Change (24h): ${data.tradeData.price_change_24h_percent}%\n`; - output += `- Price Change (12h): ${data.tradeData.price_change_12h_percent}%\n`; - output += `- Volume (24h USD): $${toBN(data.tradeData.volume_24h_usd).toFixed(2)}\n`; - output += `- Current Price: $${toBN(data.tradeData.price).toFixed(2)}\n\n`; - - // Holder Distribution Trend - output += `**Holder Distribution Trend:** ${data.holderDistributionTrend}\n\n`; - - // High-Value Holders - output += `**High-Value Holders (>$5 USD):**\n`; - if (data.highValueHolders.length === 0) { - output += `- No high-value holders found or data not available.\n`; - } else { - data.highValueHolders.forEach((holder) => { - output += `- ${holder.holderAddress}: $${holder.balanceUsd}\n`; - }); - } - output += `\n`; - - // Recent Trades - output += `**Recent Trades (Last 24h):** ${data.recentTrades ? "Yes" : "No"}\n\n`; - - // High-Supply Holders - output += `**Holders with >2% Supply:** ${data.highSupplyHoldersCount}\n\n`; - - // DexScreener Status - output += `**DexScreener Listing:** ${data.isDexScreenerListed ? "Yes" : "No"}\n`; - if (data.isDexScreenerListed) { - output += `- Listing Type: ${data.isDexScreenerPaid ? "Paid" : "Free"}\n`; - output += `- Number of DexPairs: ${data.dexScreenerData.pairs.length}\n\n`; - output += `**DexScreener Pairs:**\n`; - data.dexScreenerData.pairs.forEach((pair, index) => { - output += `\n**Pair ${index + 1}:**\n`; - output += `- DEX: ${pair.dexId}\n`; - output += `- URL: ${pair.url}\n`; - output += `- Price USD: $${toBN(pair.priceUsd).toFixed(6)}\n`; - output += `- Volume (24h USD): $${toBN(pair.volume.h24).toFixed(2)}\n`; - output += `- Boosts Active: ${pair.boosts && pair.boosts.active}\n`; - output += `- Liquidity USD: $${toBN(pair.liquidity.usd).toFixed(2)}\n`; - }); - } - output += `\n`; - - console.log("Formatted token data:", output); - return output; - } - - async getFormattedTokenReport(): Promise { - try { - console.log("Generating formatted token report..."); - const processedData = await this.getProcessedTokenData(); - return this.formatTokenData(processedData); - } catch (error) { - console.error("Error generating token report:", error); - return "Unable to fetch token information. Please try again later."; - } - } -} - -const tokenAddress = PROVIDER_CONFIG.TOKEN_ADDRESSES.Example; - -const connection = new Connection(PROVIDER_CONFIG.DEFAULT_RPC); -const tokenProvider: Provider = { - get: async ( - runtime: IAgentRuntime, - _message: Memory, - _state?: State - ): Promise => { - try { - const { publicKey } = await getWalletKey(runtime, false); - - const walletProvider = new WalletProvider(connection, publicKey); - - const provider = new TokenProvider( - tokenAddress, - walletProvider, - runtime.cacheManager - ); - - return provider.getFormattedTokenReport(); - } catch (error) { - console.error("Error fetching token data:", error); - return "Unable to fetch token information. Please try again later."; - } - }, -}; - -export { tokenProvider }; diff --git a/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts b/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts deleted file mode 100644 index 034dddc299..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/tokenUtils.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token"; -import { Connection, PublicKey } from "@solana/web3.js"; - -export async function getTokenPriceInSol(tokenSymbol: string): Promise { - const response = await fetch( - `https://price.jup.ag/v6/price?ids=${tokenSymbol}` - ); - const data = await response.json(); - return data.data[tokenSymbol].price; -} - -async function getTokenBalance( - connection: Connection, - walletPublicKey: PublicKey, - tokenMintAddress: PublicKey -): Promise { - const tokenAccountAddress = await getAssociatedTokenAddress( - tokenMintAddress, - walletPublicKey - ); - - try { - const tokenAccount = await getAccount(connection, tokenAccountAddress); - const tokenAmount = tokenAccount.amount as unknown as number; - return tokenAmount; - } catch (error) { - console.error( - `Error retrieving balance for token: ${tokenMintAddress.toBase58()}`, - error - ); - return 0; - } -} - -async function getTokenBalances( - connection: Connection, - walletPublicKey: PublicKey -): Promise<{ [tokenName: string]: number }> { - const tokenBalances: { [tokenName: string]: number } = {}; - - // Add the token mint addresses you want to retrieve balances for - const tokenMintAddresses = [ - new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC - new PublicKey("So11111111111111111111111111111111111111112"), // SOL - // Add more token mint addresses as needed - ]; - - for (const mintAddress of tokenMintAddresses) { - const tokenName = getTokenName(mintAddress); - const balance = await getTokenBalance( - connection, - walletPublicKey, - mintAddress - ); - tokenBalances[tokenName] = balance; - } - - return tokenBalances; -} - -function getTokenName(mintAddress: PublicKey): string { - // Implement a mapping of mint addresses to token names - const tokenNameMap: { [mintAddress: string]: string } = { - EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: "USDC", - So11111111111111111111111111111111111111112: "SOL", - // Add more token mint addresses and their corresponding names - }; - - return tokenNameMap[mintAddress.toBase58()] || "Unknown Token"; -} - -export { getTokenBalance, getTokenBalances }; diff --git a/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts b/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts deleted file mode 100644 index 931cd9b44d..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/trustScoreProvider.ts +++ /dev/null @@ -1,740 +0,0 @@ -import { - ProcessedTokenData, - TokenSecurityData, - // TokenTradeData, - // DexScreenerData, - // DexScreenerPair, - // HolderData, -} from "../types/token.ts"; -import { Connection, PublicKey } from "@solana/web3.js"; -import { getAssociatedTokenAddress } from "@solana/spl-token"; -import { TokenProvider } from "./token.ts"; -import { WalletProvider } from "./wallet.ts"; -import { SimulationSellingService } from "./simulationSellingService.ts"; -import { - TrustScoreDatabase, - RecommenderMetrics, - TokenPerformance, - TradePerformance, - TokenRecommendation, -} from "@elizaos/plugin-trustdb"; -import { settings } from "@elizaos/core"; -import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; -import { v4 as uuidv4 } from "uuid"; - -const Wallet = settings.MAIN_WALLET_ADDRESS; -interface TradeData { - buy_amount: number; - is_simulation: boolean; -} -interface sellDetails { - sell_amount: number; - sell_recommender_id: string | null; -} -interface _RecommendationGroup { - recommendation: any; - trustScore: number; -} - -interface RecommenderData { - recommenderId: string; - trustScore: number; - riskScore: number; - consistencyScore: number; - recommenderMetrics: RecommenderMetrics; -} - -interface TokenRecommendationSummary { - tokenAddress: string; - averageTrustScore: number; - averageRiskScore: number; - averageConsistencyScore: number; - recommenders: RecommenderData[]; -} -export class TrustScoreManager { - private tokenProvider: TokenProvider; - private trustScoreDb: TrustScoreDatabase; - private simulationSellingService: SimulationSellingService; - private connection: Connection; - private baseMint: PublicKey; - private DECAY_RATE = 0.95; - private MAX_DECAY_DAYS = 30; - private backend; - private backendToken; - constructor( - runtime: IAgentRuntime, - tokenProvider: TokenProvider, - trustScoreDb: TrustScoreDatabase - ) { - this.tokenProvider = tokenProvider; - this.trustScoreDb = trustScoreDb; - this.connection = new Connection(runtime.getSetting("RPC_URL")); - this.baseMint = new PublicKey( - runtime.getSetting("BASE_MINT") || - "So11111111111111111111111111111111111111112" - ); - this.backend = runtime.getSetting("BACKEND_URL"); - this.backendToken = runtime.getSetting("BACKEND_TOKEN"); - this.simulationSellingService = new SimulationSellingService( - runtime, - this.trustScoreDb - ); - } - - //getRecommenederBalance - async getRecommenederBalance(recommenderWallet: string): Promise { - try { - const tokenAta = await getAssociatedTokenAddress( - new PublicKey(recommenderWallet), - this.baseMint - ); - const tokenBalInfo = - await this.connection.getTokenAccountBalance(tokenAta); - const tokenBalance = tokenBalInfo.value.amount; - const balance = parseFloat(tokenBalance); - return balance; - } catch (error) { - console.error("Error fetching balance", error); - return 0; - } - } - - /** - * Generates and saves trust score based on processed token data and user recommendations. - * @param tokenAddress The address of the token to analyze. - * @param recommenderId The UUID of the recommender. - * @returns An object containing TokenPerformance and RecommenderMetrics. - */ - async generateTrustScore( - tokenAddress: string, - recommenderId: string, - recommenderWallet: string - ): Promise<{ - tokenPerformance: TokenPerformance; - recommenderMetrics: RecommenderMetrics; - }> { - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - console.log(`Fetched processed token data for token: ${tokenAddress}`); - - const recommenderMetrics = - await this.trustScoreDb.getRecommenderMetrics(recommenderId); - - const isRapidDump = await this.isRapidDump(tokenAddress); - const sustainedGrowth = await this.sustainedGrowth(tokenAddress); - const suspiciousVolume = await this.suspiciousVolume(tokenAddress); - const balance = await this.getRecommenederBalance(recommenderWallet); - const virtualConfidence = balance / 1000000; // TODO: create formula to calculate virtual confidence based on user balance - const lastActive = recommenderMetrics.lastActiveDate; - const now = new Date(); - const inactiveDays = Math.floor( - (now.getTime() - lastActive.getTime()) / (1000 * 60 * 60 * 24) - ); - const decayFactor = Math.pow( - this.DECAY_RATE, - Math.min(inactiveDays, this.MAX_DECAY_DAYS) - ); - const decayedScore = recommenderMetrics.trustScore * decayFactor; - const validationTrustScore = - this.trustScoreDb.calculateValidationTrust(tokenAddress); - - return { - tokenPerformance: { - tokenAddress: - processedData.dexScreenerData.pairs[0]?.baseToken.address || - "", - priceChange24h: - processedData.tradeData.price_change_24h_percent, - volumeChange24h: processedData.tradeData.volume_24h, - trade_24h_change: - processedData.tradeData.trade_24h_change_percent, - liquidity: - processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, - liquidityChange24h: 0, - holderChange24h: - processedData.tradeData.unique_wallet_24h_change_percent, - rugPull: false, - isScam: processedData.tokenCodex.isScam, - marketCapChange24h: 0, - sustainedGrowth: sustainedGrowth, - rapidDump: isRapidDump, - suspiciousVolume: suspiciousVolume, - validationTrust: validationTrustScore, - balance: balance, - initialMarketCap: - processedData.dexScreenerData.pairs[0]?.marketCap || 0, - lastUpdated: new Date(), - symbol: "", - }, - recommenderMetrics: { - recommenderId: recommenderId, - trustScore: recommenderMetrics.trustScore, - totalRecommendations: recommenderMetrics.totalRecommendations, - successfulRecs: recommenderMetrics.successfulRecs, - avgTokenPerformance: recommenderMetrics.avgTokenPerformance, - riskScore: recommenderMetrics.riskScore, - consistencyScore: recommenderMetrics.consistencyScore, - virtualConfidence: virtualConfidence, - lastActiveDate: now, - trustDecay: decayedScore, - lastUpdated: new Date(), - }, - }; - } - - async updateRecommenderMetrics( - recommenderId: string, - tokenPerformance: TokenPerformance, - recommenderWallet: string - ): Promise { - const recommenderMetrics = - await this.trustScoreDb.getRecommenderMetrics(recommenderId); - - const totalRecommendations = - recommenderMetrics.totalRecommendations + 1; - const successfulRecs = tokenPerformance.rugPull - ? recommenderMetrics.successfulRecs - : recommenderMetrics.successfulRecs + 1; - const avgTokenPerformance = - (recommenderMetrics.avgTokenPerformance * - recommenderMetrics.totalRecommendations + - tokenPerformance.priceChange24h) / - totalRecommendations; - - const overallTrustScore = this.calculateTrustScore( - tokenPerformance, - recommenderMetrics - ); - const riskScore = this.calculateOverallRiskScore( - tokenPerformance, - recommenderMetrics - ); - const consistencyScore = this.calculateConsistencyScore( - tokenPerformance, - recommenderMetrics - ); - - const balance = await this.getRecommenederBalance(recommenderWallet); - const virtualConfidence = balance / 1000000; // TODO: create formula to calculate virtual confidence based on user balance - const lastActive = recommenderMetrics.lastActiveDate; - const now = new Date(); - const inactiveDays = Math.floor( - (now.getTime() - lastActive.getTime()) / (1000 * 60 * 60 * 24) - ); - const decayFactor = Math.pow( - this.DECAY_RATE, - Math.min(inactiveDays, this.MAX_DECAY_DAYS) - ); - const decayedScore = recommenderMetrics.trustScore * decayFactor; - - const newRecommenderMetrics: RecommenderMetrics = { - recommenderId: recommenderId, - trustScore: overallTrustScore, - totalRecommendations: totalRecommendations, - successfulRecs: successfulRecs, - avgTokenPerformance: avgTokenPerformance, - riskScore: riskScore, - consistencyScore: consistencyScore, - virtualConfidence: virtualConfidence, - lastActiveDate: new Date(), - trustDecay: decayedScore, - lastUpdated: new Date(), - }; - - await this.trustScoreDb.updateRecommenderMetrics(newRecommenderMetrics); - } - - calculateTrustScore( - tokenPerformance: TokenPerformance, - recommenderMetrics: RecommenderMetrics - ): number { - const riskScore = this.calculateRiskScore(tokenPerformance); - const consistencyScore = this.calculateConsistencyScore( - tokenPerformance, - recommenderMetrics - ); - - return (riskScore + consistencyScore) / 2; - } - - calculateOverallRiskScore( - tokenPerformance: TokenPerformance, - recommenderMetrics: RecommenderMetrics - ) { - const riskScore = this.calculateRiskScore(tokenPerformance); - const consistencyScore = this.calculateConsistencyScore( - tokenPerformance, - recommenderMetrics - ); - - return (riskScore + consistencyScore) / 2; - } - - calculateRiskScore(tokenPerformance: TokenPerformance): number { - let riskScore = 0; - if (tokenPerformance.rugPull) { - riskScore += 10; - } - if (tokenPerformance.isScam) { - riskScore += 10; - } - if (tokenPerformance.rapidDump) { - riskScore += 5; - } - if (tokenPerformance.suspiciousVolume) { - riskScore += 5; - } - return riskScore; - } - - calculateConsistencyScore( - tokenPerformance: TokenPerformance, - recommenderMetrics: RecommenderMetrics - ): number { - const avgTokenPerformance = recommenderMetrics.avgTokenPerformance; - const priceChange24h = tokenPerformance.priceChange24h; - - return Math.abs(priceChange24h - avgTokenPerformance); - } - - async suspiciousVolume(tokenAddress: string): Promise { - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - const unique_wallet_24h = processedData.tradeData.unique_wallet_24h; - const volume_24h = processedData.tradeData.volume_24h; - const suspiciousVolume = unique_wallet_24h / volume_24h > 0.5; - console.log(`Fetched processed token data for token: ${tokenAddress}`); - return suspiciousVolume; - } - - async sustainedGrowth(tokenAddress: string): Promise { - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - console.log(`Fetched processed token data for token: ${tokenAddress}`); - - return processedData.tradeData.volume_24h_change_percent > 50; - } - - async isRapidDump(tokenAddress: string): Promise { - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - console.log(`Fetched processed token data for token: ${tokenAddress}`); - - return processedData.tradeData.trade_24h_change_percent < -50; - } - - async checkTrustScore(tokenAddress: string): Promise { - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - console.log(`Fetched processed token data for token: ${tokenAddress}`); - - return { - ownerBalance: processedData.security.ownerBalance, - creatorBalance: processedData.security.creatorBalance, - ownerPercentage: processedData.security.ownerPercentage, - creatorPercentage: processedData.security.creatorPercentage, - top10HolderBalance: processedData.security.top10HolderBalance, - top10HolderPercent: processedData.security.top10HolderPercent, - }; - } - - /** - * Creates a TradePerformance object based on token data and recommender. - * @param tokenAddress The address of the token. - * @param recommenderId The UUID of the recommender. - * @param data ProcessedTokenData. - * @returns TradePerformance object. - */ - async createTradePerformance( - runtime: IAgentRuntime, - tokenAddress: string, - recommenderId: string, - data: TradeData - ): Promise { - const recommender = - await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( - recommenderId - ); - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - const wallet = new WalletProvider( - this.connection, - new PublicKey(Wallet!) - ); - - let tokensBalance = 0; - const prices = await wallet.fetchPrices(runtime); - const solPrice = prices.solana.usd; - const buySol = data.buy_amount / parseFloat(solPrice); - const buy_value_usd = data.buy_amount * processedData.tradeData.price; - const token = await this.tokenProvider.fetchTokenTradeData(); - const tokenCodex = await this.tokenProvider.fetchTokenCodex(); - const tokenPrice = token.price; - tokensBalance = buy_value_usd / tokenPrice; - - const creationData = { - token_address: tokenAddress, - recommender_id: recommender.id, - buy_price: processedData.tradeData.price, - sell_price: 0, - buy_timeStamp: new Date().toISOString(), - sell_timeStamp: "", - buy_amount: data.buy_amount, - sell_amount: 0, - buy_sol: buySol, - received_sol: 0, - buy_value_usd: buy_value_usd, - sell_value_usd: 0, - profit_usd: 0, - profit_percent: 0, - buy_market_cap: - processedData.dexScreenerData.pairs[0]?.marketCap || 0, - sell_market_cap: 0, - market_cap_change: 0, - buy_liquidity: - processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, - sell_liquidity: 0, - liquidity_change: 0, - last_updated: new Date().toISOString(), - rapidDump: false, - }; - this.trustScoreDb.addTradePerformance(creationData, data.is_simulation); - // generate unique uuid for each TokenRecommendation - const tokenUUId = uuidv4(); - const tokenRecommendation: TokenRecommendation = { - id: tokenUUId, - recommenderId: recommenderId, - tokenAddress: tokenAddress, - timestamp: new Date(), - initialMarketCap: - processedData.dexScreenerData.pairs[0]?.marketCap || 0, - initialLiquidity: - processedData.dexScreenerData.pairs[0]?.liquidity?.usd || 0, - initialPrice: processedData.tradeData.price, - }; - this.trustScoreDb.addTokenRecommendation(tokenRecommendation); - - this.trustScoreDb.upsertTokenPerformance({ - tokenAddress: tokenAddress, - symbol: processedData.tokenCodex.symbol, - priceChange24h: processedData.tradeData.price_change_24h_percent, - volumeChange24h: processedData.tradeData.volume_24h, - trade_24h_change: processedData.tradeData.trade_24h_change_percent, - liquidity: - processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, - liquidityChange24h: 0, - holderChange24h: - processedData.tradeData.unique_wallet_24h_change_percent, - rugPull: false, - isScam: tokenCodex.isScam, - marketCapChange24h: 0, - sustainedGrowth: false, - rapidDump: false, - suspiciousVolume: false, - validationTrust: 0, - balance: tokensBalance, - initialMarketCap: - processedData.dexScreenerData.pairs[0]?.marketCap || 0, - lastUpdated: new Date(), - }); - - if (data.is_simulation) { - // If the trade is a simulation update the balance - this.trustScoreDb.updateTokenBalance(tokenAddress, tokensBalance); - // generate some random hash for simulations - const hash = Math.random().toString(36).substring(7); - const transaction = { - tokenAddress: tokenAddress, - type: "buy" as "buy" | "sell", - transactionHash: hash, - amount: data.buy_amount, - price: processedData.tradeData.price, - isSimulation: true, - timestamp: new Date().toISOString(), - }; - this.trustScoreDb.addTransaction(transaction); - } - this.simulationSellingService.processTokenPerformance( - tokenAddress, - recommenderId - ); - // api call to update trade performance - this.createTradeInBe(tokenAddress, recommenderId, data); - return creationData; - } - - async delay(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - - async createTradeInBe( - tokenAddress: string, - recommenderId: string, - data: TradeData, - retries = 3, - delayMs = 2000 - ) { - for (let attempt = 1; attempt <= retries; attempt++) { - try { - await fetch( - `${this.backend}/api/updaters/createTradePerformance`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${this.backendToken}`, - }, - body: JSON.stringify({ - tokenAddress: tokenAddress, - tradeData: data, - recommenderId: recommenderId, - }), - } - ); - // If the request is successful, exit the loop - return; - } catch (error) { - console.error( - `Attempt ${attempt} failed: Error creating trade in backend`, - error - ); - if (attempt < retries) { - console.log(`Retrying in ${delayMs} ms...`); - await this.delay(delayMs); // Wait for the specified delay before retrying - } else { - console.error("All attempts failed."); - } - } - } - } - - /** - * Updates a trade with sell details. - * @param tokenAddress The address of the token. - * @param recommenderId The UUID of the recommender. - * @param buyTimeStamp The timestamp when the buy occurred. - * @param sellDetails An object containing sell-related details. - * @param isSimulation Whether the trade is a simulation. If true, updates in simulation_trade; otherwise, in trade. - * @returns boolean indicating success. - */ - - async updateSellDetails( - runtime: IAgentRuntime, - tokenAddress: string, - recommenderId: string, - sellTimeStamp: string, - sellDetails: sellDetails, - isSimulation: boolean - ) { - const recommender = - await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( - recommenderId - ); - const processedData: ProcessedTokenData = - await this.tokenProvider.getProcessedTokenData(); - const wallet = new WalletProvider( - this.connection, - new PublicKey(Wallet!) - ); - const prices = await wallet.fetchPrices(runtime); - const solPrice = prices.solana.usd; - const sellSol = sellDetails.sell_amount / parseFloat(solPrice); - const sell_value_usd = - sellDetails.sell_amount * processedData.tradeData.price; - const trade = await this.trustScoreDb.getLatestTradePerformance( - tokenAddress, - recommender.id, - isSimulation - ); - const buyTimeStamp = trade.buy_timeStamp; - const marketCap = - processedData.dexScreenerData.pairs[0]?.marketCap || 0; - const liquidity = - processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0; - const sell_price = processedData.tradeData.price; - const profit_usd = sell_value_usd - trade.buy_value_usd; - const profit_percent = (profit_usd / trade.buy_value_usd) * 100; - - const market_cap_change = marketCap - trade.buy_market_cap; - const liquidity_change = liquidity - trade.buy_liquidity; - - const isRapidDump = await this.isRapidDump(tokenAddress); - - const sellDetailsData = { - sell_price: sell_price, - sell_timeStamp: sellTimeStamp, - sell_amount: sellDetails.sell_amount, - received_sol: sellSol, - sell_value_usd: sell_value_usd, - profit_usd: profit_usd, - profit_percent: profit_percent, - sell_market_cap: marketCap, - market_cap_change: market_cap_change, - sell_liquidity: liquidity, - liquidity_change: liquidity_change, - rapidDump: isRapidDump, - sell_recommender_id: sellDetails.sell_recommender_id || null, - }; - this.trustScoreDb.updateTradePerformanceOnSell( - tokenAddress, - recommender.id, - buyTimeStamp, - sellDetailsData, - isSimulation - ); - if (isSimulation) { - // If the trade is a simulation update the balance - const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); - const tokenBalance = oldBalance - sellDetails.sell_amount; - this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); - // generate some random hash for simulations - const hash = Math.random().toString(36).substring(7); - const transaction = { - tokenAddress: tokenAddress, - type: "sell" as "buy" | "sell", - transactionHash: hash, - amount: sellDetails.sell_amount, - price: processedData.tradeData.price, - isSimulation: true, - timestamp: new Date().toISOString(), - }; - this.trustScoreDb.addTransaction(transaction); - } - - return sellDetailsData; - } - - // get all recommendations - async getRecommendations( - startDate: Date, - endDate: Date - ): Promise> { - const recommendations = this.trustScoreDb.getRecommendationsByDateRange( - startDate, - endDate - ); - - // Group recommendations by tokenAddress - const groupedRecommendations = recommendations.reduce( - (acc, recommendation) => { - const { tokenAddress } = recommendation; - if (!acc[tokenAddress]) acc[tokenAddress] = []; - acc[tokenAddress].push(recommendation); - return acc; - }, - {} as Record> - ); - - const result = Object.keys(groupedRecommendations).map( - (tokenAddress) => { - const tokenRecommendations = - groupedRecommendations[tokenAddress]; - - // Initialize variables to compute averages - let totalTrustScore = 0; - let totalRiskScore = 0; - let totalConsistencyScore = 0; - const recommenderData = []; - - tokenRecommendations.forEach((recommendation) => { - const tokenPerformance = - this.trustScoreDb.getTokenPerformance( - recommendation.tokenAddress - ); - const recommenderMetrics = - this.trustScoreDb.getRecommenderMetrics( - recommendation.recommenderId - ); - - const trustScore = this.calculateTrustScore( - tokenPerformance, - recommenderMetrics - ); - const consistencyScore = this.calculateConsistencyScore( - tokenPerformance, - recommenderMetrics - ); - const riskScore = this.calculateRiskScore(tokenPerformance); - - // Accumulate scores for averaging - totalTrustScore += trustScore; - totalRiskScore += riskScore; - totalConsistencyScore += consistencyScore; - - recommenderData.push({ - recommenderId: recommendation.recommenderId, - trustScore, - riskScore, - consistencyScore, - recommenderMetrics, - }); - }); - - // Calculate averages for this token - const averageTrustScore = - totalTrustScore / tokenRecommendations.length; - const averageRiskScore = - totalRiskScore / tokenRecommendations.length; - const averageConsistencyScore = - totalConsistencyScore / tokenRecommendations.length; - - return { - tokenAddress, - averageTrustScore, - averageRiskScore, - averageConsistencyScore, - recommenders: recommenderData, - }; - } - ); - - // Sort recommendations by the highest average trust score - result.sort((a, b) => b.averageTrustScore - a.averageTrustScore); - - return result; - } -} - -export const trustScoreProvider: Provider = { - async get( - runtime: IAgentRuntime, - message: Memory, - _state?: State - ): Promise { - try { - const trustScoreDb = new TrustScoreDatabase( - runtime.databaseAdapter.db - ); - - // Get the user ID from the message - const userId = message.userId; - - if (!userId) { - console.error("User ID is missing from the message"); - return ""; - } - - // Get the recommender metrics for the user - const recommenderMetrics = - await trustScoreDb.getRecommenderMetrics(userId); - - if (!recommenderMetrics) { - console.error("No recommender metrics found for user:", userId); - return ""; - } - - // Compute the trust score - const trustScore = recommenderMetrics.trustScore; - - const user = await runtime.databaseAdapter.getAccountById(userId); - - // Format the trust score string - const trustScoreString = `${user.name}'s trust score: ${trustScore.toFixed(2)}`; - - return trustScoreString; - } catch (error) { - console.error("Error in trust score provider:", error.message); - return `Failed to fetch trust score: ${error instanceof Error ? error.message : "Unknown error"}`; - } - }, -}; diff --git a/packages/plugin-solana-agentkit/src/providers/wallet.ts b/packages/plugin-solana-agentkit/src/providers/wallet.ts deleted file mode 100644 index 7e3c55580b..0000000000 --- a/packages/plugin-solana-agentkit/src/providers/wallet.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { IAgentRuntime, Memory, Provider, State } from "@elizaos/core"; -import { Connection, PublicKey } from "@solana/web3.js"; -import BigNumber from "bignumber.js"; -import NodeCache from "node-cache"; -import { getWalletKey } from "../keypairUtils"; - -// Provider configuration -const PROVIDER_CONFIG = { - BIRDEYE_API: "https://public-api.birdeye.so", - MAX_RETRIES: 3, - RETRY_DELAY: 2000, - DEFAULT_RPC: "https://api.mainnet-beta.solana.com", - GRAPHQL_ENDPOINT: "https://graph.codex.io/graphql", - TOKEN_ADDRESSES: { - SOL: "So11111111111111111111111111111111111111112", - BTC: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh", - ETH: "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", - }, -}; - -export interface Item { - name: string; - address: string; - symbol: string; - decimals: number; - balance: string; - uiAmount: string; - priceUsd: string; - valueUsd: string; - valueSol?: string; -} - -interface WalletPortfolio { - totalUsd: string; - totalSol?: string; - items: Array; -} - -interface _BirdEyePriceData { - data: { - [key: string]: { - price: number; - priceChange24h: number; - }; - }; -} - -interface Prices { - solana: { usd: string }; - bitcoin: { usd: string }; - ethereum: { usd: string }; -} - -export class WalletProvider { - private cache: NodeCache; - - constructor( - private connection: Connection, - private walletPublicKey: PublicKey - ) { - this.cache = new NodeCache({ stdTTL: 300 }); // Cache TTL set to 5 minutes - } - - private async fetchWithRetry( - runtime, - url: string, - options: RequestInit = {} - ): Promise { - let lastError: Error; - - for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { - try { - const response = await fetch(url, { - ...options, - headers: { - Accept: "application/json", - "x-chain": "solana", - "X-API-KEY": - runtime.getSetting("BIRDEYE_API_KEY", "") || "", - ...options.headers, - }, - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error( - `HTTP error! status: ${response.status}, message: ${errorText}` - ); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error(`Attempt ${i + 1} failed:`, error); - lastError = error; - if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { - const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); - await new Promise((resolve) => setTimeout(resolve, delay)); - continue; - } - } - } - - console.error( - "All attempts failed. Throwing the last error:", - lastError - ); - throw lastError; - } - - async fetchPortfolioValue(runtime): Promise { - try { - const cacheKey = `portfolio-${this.walletPublicKey.toBase58()}`; - const cachedValue = this.cache.get(cacheKey); - - if (cachedValue) { - console.log("Cache hit for fetchPortfolioValue"); - return cachedValue; - } - console.log("Cache miss for fetchPortfolioValue"); - - const walletData = await this.fetchWithRetry( - runtime, - `${PROVIDER_CONFIG.BIRDEYE_API}/v1/wallet/token_list?wallet=${this.walletPublicKey.toBase58()}` - ); - - if (!walletData?.success || !walletData?.data) { - console.error("No portfolio data available", walletData); - throw new Error("No portfolio data available"); - } - - const data = walletData.data; - const totalUsd = new BigNumber(data.totalUsd.toString()); - const prices = await this.fetchPrices(runtime); - const solPriceInUSD = new BigNumber(prices.solana.usd.toString()); - - const items = data.items.map((item: any) => ({ - ...item, - valueSol: new BigNumber(item.valueUsd || 0) - .div(solPriceInUSD) - .toFixed(6), - name: item.name || "Unknown", - symbol: item.symbol || "Unknown", - priceUsd: item.priceUsd || "0", - valueUsd: item.valueUsd || "0", - })); - - const totalSol = totalUsd.div(solPriceInUSD); - const portfolio = { - totalUsd: totalUsd.toString(), - totalSol: totalSol.toFixed(6), - items: items.sort((a, b) => - new BigNumber(b.valueUsd) - .minus(new BigNumber(a.valueUsd)) - .toNumber() - ), - }; - this.cache.set(cacheKey, portfolio); - return portfolio; - } catch (error) { - console.error("Error fetching portfolio:", error); - throw error; - } - } - - async fetchPortfolioValueCodex(runtime): Promise { - try { - const cacheKey = `portfolio-${this.walletPublicKey.toBase58()}`; - const cachedValue = await this.cache.get(cacheKey); - - if (cachedValue) { - console.log("Cache hit for fetchPortfolioValue"); - return cachedValue; - } - console.log("Cache miss for fetchPortfolioValue"); - - const query = ` - query Balances($walletId: String!, $cursor: String) { - balances(input: { walletId: $walletId, cursor: $cursor }) { - cursor - items { - walletId - tokenId - balance - shiftedBalance - } - } - } - `; - - const variables = { - walletId: `${this.walletPublicKey.toBase58()}:${1399811149}`, - cursor: null, - }; - - const response = await fetch(PROVIDER_CONFIG.GRAPHQL_ENDPOINT, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: - runtime.getSetting("CODEX_API_KEY", "") || "", - }, - body: JSON.stringify({ - query, - variables, - }), - }).then((res) => res.json()); - - const data = response.data?.data?.balances?.items; - - if (!data || data.length === 0) { - console.error("No portfolio data available", data); - throw new Error("No portfolio data available"); - } - - // Fetch token prices - const prices = await this.fetchPrices(runtime); - const solPriceInUSD = new BigNumber(prices.solana.usd.toString()); - - // Reformat items - const items: Item[] = data.map((item: any) => { - return { - name: "Unknown", - address: item.tokenId.split(":")[0], - symbol: item.tokenId.split(":")[0], - decimals: 6, - balance: item.balance, - uiAmount: item.shiftedBalance.toString(), - priceUsd: "", - valueUsd: "", - valueSol: "", - }; - }); - - // Calculate total portfolio value - const totalUsd = items.reduce( - (sum, item) => sum.plus(new BigNumber(item.valueUsd)), - new BigNumber(0) - ); - - const totalSol = totalUsd.div(solPriceInUSD); - - const portfolio: WalletPortfolio = { - totalUsd: totalUsd.toFixed(6), - totalSol: totalSol.toFixed(6), - items: items.sort((a, b) => - new BigNumber(b.valueUsd) - .minus(new BigNumber(a.valueUsd)) - .toNumber() - ), - }; - - // Cache the portfolio for future requests - await this.cache.set(cacheKey, portfolio, 60 * 1000); // Cache for 1 minute - - return portfolio; - } catch (error) { - console.error("Error fetching portfolio:", error); - throw error; - } - } - - async fetchPrices(runtime): Promise { - try { - const cacheKey = "prices"; - const cachedValue = this.cache.get(cacheKey); - - if (cachedValue) { - console.log("Cache hit for fetchPrices"); - return cachedValue; - } - console.log("Cache miss for fetchPrices"); - - const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; - const tokens = [SOL, BTC, ETH]; - const prices: Prices = { - solana: { usd: "0" }, - bitcoin: { usd: "0" }, - ethereum: { usd: "0" }, - }; - - for (const token of tokens) { - const response = await this.fetchWithRetry( - runtime, - `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, - { - headers: { - "x-chain": "solana", - }, - } - ); - - if (response?.data?.value) { - const price = response.data.value.toString(); - prices[ - token === SOL - ? "solana" - : token === BTC - ? "bitcoin" - : "ethereum" - ].usd = price; - } else { - console.warn(`No price data available for token: ${token}`); - } - } - - this.cache.set(cacheKey, prices); - return prices; - } catch (error) { - console.error("Error fetching prices:", error); - throw error; - } - } - - formatPortfolio( - runtime, - portfolio: WalletPortfolio, - prices: Prices - ): string { - let output = `${runtime.character.description}\n`; - output += `Wallet Address: ${this.walletPublicKey.toBase58()}\n\n`; - - const totalUsdFormatted = new BigNumber(portfolio.totalUsd).toFixed(2); - const totalSolFormatted = portfolio.totalSol; - - output += `Total Value: $${totalUsdFormatted} (${totalSolFormatted} SOL)\n\n`; - output += "Token Balances:\n"; - - const nonZeroItems = portfolio.items.filter((item) => - new BigNumber(item.uiAmount).isGreaterThan(0) - ); - - if (nonZeroItems.length === 0) { - output += "No tokens found with non-zero balance\n"; - } else { - for (const item of nonZeroItems) { - const valueUsd = new BigNumber(item.valueUsd).toFixed(2); - output += `${item.name} (${item.symbol}): ${new BigNumber( - item.uiAmount - ).toFixed(6)} ($${valueUsd} | ${item.valueSol} SOL)\n`; - } - } - - output += "\nMarket Prices:\n"; - output += `SOL: $${new BigNumber(prices.solana.usd).toFixed(2)}\n`; - output += `BTC: $${new BigNumber(prices.bitcoin.usd).toFixed(2)}\n`; - output += `ETH: $${new BigNumber(prices.ethereum.usd).toFixed(2)}\n`; - - return output; - } - - async getFormattedPortfolio(runtime): Promise { - try { - const [portfolio, prices] = await Promise.all([ - this.fetchPortfolioValue(runtime), - this.fetchPrices(runtime), - ]); - - return this.formatPortfolio(runtime, portfolio, prices); - } catch (error) { - console.error("Error generating portfolio report:", error); - return "Unable to fetch wallet information. Please try again later."; - } - } -} - -const walletProvider: Provider = { - get: async ( - runtime: IAgentRuntime, - _message: Memory, - _state?: State - ): Promise => { - try { - const { publicKey } = await getWalletKey(runtime, false); - - const connection = new Connection( - runtime.getSetting("RPC_URL") || PROVIDER_CONFIG.DEFAULT_RPC - ); - - const provider = new WalletProvider(connection, publicKey); - - return await provider.getFormattedPortfolio(runtime); - } catch (error) { - console.error("Error in wallet provider:", error); - return null; - } - }, -}; - -// Module exports -export { walletProvider }; From 5ae88bcfcdf3e3e67fad9b5ef5727487bda231e4 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 20:33:28 +0800 Subject: [PATCH 03/58] feat: handle token deploy --- .../src/actions/createToken.ts | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts index 74669e52fb..a225cc1bbb 100644 --- a/packages/plugin-solana-agentkit/src/actions/createToken.ts +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -1,7 +1,9 @@ import { ActionExample, + composeContext, Content, elizaLogger, + generateObjectDeprecated, HandlerCallback, IAgentRuntime, Memory, @@ -10,6 +12,8 @@ import { type Action, } from "@elizaos/core"; +import { SolanaAgentKit } from "solana-agent-kit"; + export interface CreateTokenContent extends Content { name: string; uri: string; @@ -32,6 +36,30 @@ function isCreateTokenContent( ); } +const createTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. + +Example response: +\`\`\`json +{ + "name": "Example Token", + "symbol": "EXMPL", + "uri": "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/CompressedCoil/image.png", + "decimals": 18, + "initialSupply": 1000000, +} +\`\`\` + +{{recentMessages}} + +Given the recent messages, extract the following information about the requested token transfer: +- Token name +- Token symbol +- Token uri +- Token decimals +- Token initialSupply + +Respond with a JSON markdown block containing only the extracted values.`; + export default { name: "CREATE_TOKEN", similes: ["DEPLOY_TOKEN"], @@ -45,7 +73,75 @@ export default { callback?: HandlerCallback ): Promise => { elizaLogger.log("Starting CREATE_TOKEN handler..."); - return true; + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + // Compose transfer context + const transferContext = composeContext({ + state, + template: createTemplate, + }); + + // Generate transfer content + const content = await generateObjectDeprecated({ + runtime, + context: transferContext, + modelClass: ModelClass.LARGE, + }); + + // Validate transfer content + if (!isCreateTokenContent(runtime, content)) { + elizaLogger.error("Invalid content for CREATE_TOKEN action."); + if (callback) { + callback({ + text: "Unable to process create token request. Invalid content provided.", + content: { error: "Invalid creat token content" }, + }); + } + return false; + } + + elizaLogger.log("Init solana agent kit..."); + const solanaPrivatekey = runtime.getSetting("SOLANA_PRIVATE_KEY"); + const rpc = runtime.getSetting("RPC_URL"); + const openAIKey = runtime.getSetting("OPENAI_API_KEY"); + const solanaAgentKit = new SolanaAgentKit( + solanaPrivatekey, + rpc, + openAIKey + ); + try { + const deployedAddress = solanaAgentKit.deployToken( + content.name, + content.uri, + content.symbol, + content.decimals, + content.initialSupply + ); + elizaLogger.log("Create successful: ", deployedAddress); + if (callback) { + callback({ + text: `Successfully create token ${content.name}`, + content: { + success: true, + }, + }); + } + return true; + } catch (error) { + if (callback) { + elizaLogger.error("Error during create token: ", error); + callback({ + text: `Error creating token: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } }, examples: [ [ From 347075dfbb055357a3dea270f8c70c48eb151a71 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 20:35:33 +0800 Subject: [PATCH 04/58] chore: clean up --- .../plugin-solana-agentkit/src/bignumber.ts | 9 - .../plugin-solana-agentkit/src/environment.ts | 76 ----- .../src/keypairUtils.ts | 82 ----- .../src/tests/token.test.ts | 134 -------- .../plugin-solana-agentkit/src/types/token.ts | 302 ------------------ 5 files changed, 603 deletions(-) delete mode 100644 packages/plugin-solana-agentkit/src/bignumber.ts delete mode 100644 packages/plugin-solana-agentkit/src/environment.ts delete mode 100644 packages/plugin-solana-agentkit/src/keypairUtils.ts delete mode 100644 packages/plugin-solana-agentkit/src/tests/token.test.ts delete mode 100644 packages/plugin-solana-agentkit/src/types/token.ts diff --git a/packages/plugin-solana-agentkit/src/bignumber.ts b/packages/plugin-solana-agentkit/src/bignumber.ts deleted file mode 100644 index f320676a0f..0000000000 --- a/packages/plugin-solana-agentkit/src/bignumber.ts +++ /dev/null @@ -1,9 +0,0 @@ -import BigNumber from "bignumber.js"; - -// Re-export BigNumber constructor -export const BN = BigNumber; - -// Helper function to create new BigNumber instances -export function toBN(value: string | number | BigNumber): BigNumber { - return new BigNumber(value); -} diff --git a/packages/plugin-solana-agentkit/src/environment.ts b/packages/plugin-solana-agentkit/src/environment.ts deleted file mode 100644 index e6931091c8..0000000000 --- a/packages/plugin-solana-agentkit/src/environment.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { IAgentRuntime } from "@elizaos/core"; -import { z } from "zod"; - -export const solanaEnvSchema = z - .object({ - WALLET_SECRET_SALT: z.string().optional(), - }) - .and( - z.union([ - z.object({ - WALLET_SECRET_KEY: z - .string() - .min(1, "Wallet secret key is required"), - WALLET_PUBLIC_KEY: z - .string() - .min(1, "Wallet public key is required"), - }), - z.object({ - WALLET_SECRET_SALT: z - .string() - .min(1, "Wallet secret salt is required"), - }), - ]) - ) - .and( - z.object({ - SOL_ADDRESS: z.string().min(1, "SOL address is required"), - SLIPPAGE: z.string().min(1, "Slippage is required"), - RPC_URL: z.string().min(1, "RPC URL is required"), - HELIUS_API_KEY: z.string().min(1, "Helius API key is required"), - BIRDEYE_API_KEY: z.string().min(1, "Birdeye API key is required"), - }) - ); - -export type SolanaConfig = z.infer; - -export async function validateSolanaConfig( - runtime: IAgentRuntime -): Promise { - try { - const config = { - WALLET_SECRET_SALT: - runtime.getSetting("WALLET_SECRET_SALT") || - process.env.WALLET_SECRET_SALT, - WALLET_SECRET_KEY: - runtime.getSetting("WALLET_SECRET_KEY") || - process.env.WALLET_SECRET_KEY, - WALLET_PUBLIC_KEY: - runtime.getSetting("SOLANA_PUBLIC_KEY") || - runtime.getSetting("WALLET_PUBLIC_KEY") || - process.env.WALLET_PUBLIC_KEY, - SOL_ADDRESS: - runtime.getSetting("SOL_ADDRESS") || process.env.SOL_ADDRESS, - SLIPPAGE: runtime.getSetting("SLIPPAGE") || process.env.SLIPPAGE, - RPC_URL: runtime.getSetting("RPC_URL") || process.env.RPC_URL, - HELIUS_API_KEY: - runtime.getSetting("HELIUS_API_KEY") || - process.env.HELIUS_API_KEY, - BIRDEYE_API_KEY: - runtime.getSetting("BIRDEYE_API_KEY") || - process.env.BIRDEYE_API_KEY, - }; - - return solanaEnvSchema.parse(config); - } catch (error) { - if (error instanceof z.ZodError) { - const errorMessages = error.errors - .map((err) => `${err.path.join(".")}: ${err.message}`) - .join("\n"); - throw new Error( - `Solana configuration validation failed:\n${errorMessages}` - ); - } - throw error; - } -} diff --git a/packages/plugin-solana-agentkit/src/keypairUtils.ts b/packages/plugin-solana-agentkit/src/keypairUtils.ts deleted file mode 100644 index 4aa942ebed..0000000000 --- a/packages/plugin-solana-agentkit/src/keypairUtils.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Keypair, PublicKey } from "@solana/web3.js"; -import { DeriveKeyProvider, TEEMode } from "@elizaos/plugin-tee"; -import bs58 from "bs58"; -import { IAgentRuntime } from "@elizaos/core"; - -export interface KeypairResult { - keypair?: Keypair; - publicKey?: PublicKey; -} - -/** - * Gets either a keypair or public key based on TEE mode and runtime settings - * @param runtime The agent runtime - * @param requirePrivateKey Whether to return a full keypair (true) or just public key (false) - * @returns KeypairResult containing either keypair or public key - */ -export async function getWalletKey( - runtime: IAgentRuntime, - requirePrivateKey: boolean = true -): Promise { - const teeMode = runtime.getSetting("TEE_MODE") || TEEMode.OFF; - - if (teeMode !== TEEMode.OFF) { - const walletSecretSalt = runtime.getSetting("WALLET_SECRET_SALT"); - if (!walletSecretSalt) { - throw new Error( - "WALLET_SECRET_SALT required when TEE_MODE is enabled" - ); - } - - const deriveKeyProvider = new DeriveKeyProvider(teeMode); - const deriveKeyResult = await deriveKeyProvider.deriveEd25519Keypair( - "/", - walletSecretSalt, - runtime.agentId - ); - - return requirePrivateKey - ? { keypair: deriveKeyResult.keypair } - : { publicKey: deriveKeyResult.keypair.publicKey }; - } - - // TEE mode is OFF - if (requirePrivateKey) { - const privateKeyString = - runtime.getSetting("SOLANA_PRIVATE_KEY") ?? - runtime.getSetting("WALLET_PRIVATE_KEY"); - - if (!privateKeyString) { - throw new Error("Private key not found in settings"); - } - - try { - // First try base58 - const secretKey = bs58.decode(privateKeyString); - return { keypair: Keypair.fromSecretKey(secretKey) }; - } catch (e) { - console.log("Error decoding base58 private key:", e); - try { - // Then try base64 - console.log("Try decoding base64 instead"); - const secretKey = Uint8Array.from( - Buffer.from(privateKeyString, "base64") - ); - return { keypair: Keypair.fromSecretKey(secretKey) }; - } catch (e2) { - console.error("Error decoding private key: ", e2); - throw new Error("Invalid private key format"); - } - } - } else { - const publicKeyString = - runtime.getSetting("SOLANA_PUBLIC_KEY") ?? - runtime.getSetting("WALLET_PUBLIC_KEY"); - - if (!publicKeyString) { - throw new Error("Public key not found in settings"); - } - - return { publicKey: new PublicKey(publicKeyString) }; - } -} diff --git a/packages/plugin-solana-agentkit/src/tests/token.test.ts b/packages/plugin-solana-agentkit/src/tests/token.test.ts deleted file mode 100644 index 6b799c1c23..0000000000 --- a/packages/plugin-solana-agentkit/src/tests/token.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; -import { TokenProvider } from "../providers/token.ts"; - -// Mock NodeCache -vi.mock("node-cache", () => { - return { - default: vi.fn().mockImplementation(() => ({ - set: vi.fn(), - get: vi.fn().mockReturnValue(null), - })), - }; -}); - -// Mock path module -vi.mock("path", async () => { - const actual = await vi.importActual("path"); - return { - ...(actual as any), - join: vi.fn().mockImplementation((...args) => args.join("/")), - }; -}); - -// Mock the WalletProvider -const mockWalletProvider = { - fetchPortfolioValue: vi.fn(), -}; - -// Mock the ICacheManager -const mockCacheManager = { - get: vi.fn().mockResolvedValue(null), - set: vi.fn(), -}; - -// Mock fetch globally -const mockFetch = vi.fn(); -global.fetch = mockFetch; - -describe("TokenProvider", () => { - let tokenProvider: TokenProvider; - const TEST_TOKEN_ADDRESS = "2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh"; - - beforeEach(() => { - vi.clearAllMocks(); - mockCacheManager.get.mockResolvedValue(null); - - // Create new instance of TokenProvider with mocked dependencies - tokenProvider = new TokenProvider( - TEST_TOKEN_ADDRESS, - mockWalletProvider as any, - mockCacheManager as any - ); - }); - - afterEach(() => { - vi.clearAllTimers(); - }); - - describe("Cache Management", () => { - it("should use cached data when available", async () => { - const mockData = { test: "data" }; - mockCacheManager.get.mockResolvedValueOnce(mockData); - - const result = await (tokenProvider as any).getCachedData( - "test-key" - ); - - expect(result).toEqual(mockData); - expect(mockCacheManager.get).toHaveBeenCalledTimes(1); - }); - - it("should write data to both caches", async () => { - const testData = { test: "data" }; - - await (tokenProvider as any).setCachedData("test-key", testData); - - expect(mockCacheManager.set).toHaveBeenCalledWith( - expect.stringContaining("test-key"), - testData, - expect.any(Object) - ); - }); - }); - - describe("Wallet Integration", () => { - it("should fetch tokens in wallet", async () => { - const mockItems = [ - { symbol: "SOL", address: "address1" }, - { symbol: "BTC", address: "address2" }, - ]; - - mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ - items: mockItems, - }); - - const result = await tokenProvider.getTokensInWallet({} as any); - - expect(result).toEqual(mockItems); - expect( - mockWalletProvider.fetchPortfolioValue - ).toHaveBeenCalledTimes(1); - }); - - it("should find token in wallet by symbol", async () => { - const mockItems = [ - { symbol: "SOL", address: "address1" }, - { symbol: "BTC", address: "address2" }, - ]; - - mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ - items: mockItems, - }); - - const result = await tokenProvider.getTokenFromWallet( - {} as any, - "SOL" - ); - - expect(result).toBe("address1"); - }); - - it("should return null for token not in wallet", async () => { - mockWalletProvider.fetchPortfolioValue.mockResolvedValueOnce({ - items: [], - }); - - const result = await tokenProvider.getTokenFromWallet( - {} as any, - "NONEXISTENT" - ); - - expect(result).toBeNull(); - }); - }); -}); diff --git a/packages/plugin-solana-agentkit/src/types/token.ts b/packages/plugin-solana-agentkit/src/types/token.ts deleted file mode 100644 index 1fca4c37c3..0000000000 --- a/packages/plugin-solana-agentkit/src/types/token.ts +++ /dev/null @@ -1,302 +0,0 @@ -export interface TokenSecurityData { - ownerBalance: string; - creatorBalance: string; - ownerPercentage: number; - creatorPercentage: number; - top10HolderBalance: string; - top10HolderPercent: number; -} - -export interface TokenCodex { - id: string; - address: string; - cmcId: number; - decimals: number; - name: string; - symbol: string; - totalSupply: string; - circulatingSupply: string; - imageThumbUrl: string; - blueCheckmark: boolean; - isScam: boolean; -} - -export interface TokenTradeData { - address: string; - holder: number; - market: number; - last_trade_unix_time: number; - last_trade_human_time: string; - price: number; - history_30m_price: number; - price_change_30m_percent: number; - history_1h_price: number; - price_change_1h_percent: number; - history_2h_price: number; - price_change_2h_percent: number; - history_4h_price: number; - price_change_4h_percent: number; - history_6h_price: number; - price_change_6h_percent: number; - history_8h_price: number; - price_change_8h_percent: number; - history_12h_price: number; - price_change_12h_percent: number; - history_24h_price: number; - price_change_24h_percent: number; - unique_wallet_30m: number; - unique_wallet_history_30m: number; - unique_wallet_30m_change_percent: number; - unique_wallet_1h: number; - unique_wallet_history_1h: number; - unique_wallet_1h_change_percent: number; - unique_wallet_2h: number; - unique_wallet_history_2h: number; - unique_wallet_2h_change_percent: number; - unique_wallet_4h: number; - unique_wallet_history_4h: number; - unique_wallet_4h_change_percent: number; - unique_wallet_8h: number; - unique_wallet_history_8h: number | null; - unique_wallet_8h_change_percent: number | null; - unique_wallet_24h: number; - unique_wallet_history_24h: number | null; - unique_wallet_24h_change_percent: number | null; - trade_30m: number; - trade_history_30m: number; - trade_30m_change_percent: number; - sell_30m: number; - sell_history_30m: number; - sell_30m_change_percent: number; - buy_30m: number; - buy_history_30m: number; - buy_30m_change_percent: number; - volume_30m: number; - volume_30m_usd: number; - volume_history_30m: number; - volume_history_30m_usd: number; - volume_30m_change_percent: number; - volume_buy_30m: number; - volume_buy_30m_usd: number; - volume_buy_history_30m: number; - volume_buy_history_30m_usd: number; - volume_buy_30m_change_percent: number; - volume_sell_30m: number; - volume_sell_30m_usd: number; - volume_sell_history_30m: number; - volume_sell_history_30m_usd: number; - volume_sell_30m_change_percent: number; - trade_1h: number; - trade_history_1h: number; - trade_1h_change_percent: number; - sell_1h: number; - sell_history_1h: number; - sell_1h_change_percent: number; - buy_1h: number; - buy_history_1h: number; - buy_1h_change_percent: number; - volume_1h: number; - volume_1h_usd: number; - volume_history_1h: number; - volume_history_1h_usd: number; - volume_1h_change_percent: number; - volume_buy_1h: number; - volume_buy_1h_usd: number; - volume_buy_history_1h: number; - volume_buy_history_1h_usd: number; - volume_buy_1h_change_percent: number; - volume_sell_1h: number; - volume_sell_1h_usd: number; - volume_sell_history_1h: number; - volume_sell_history_1h_usd: number; - volume_sell_1h_change_percent: number; - trade_2h: number; - trade_history_2h: number; - trade_2h_change_percent: number; - sell_2h: number; - sell_history_2h: number; - sell_2h_change_percent: number; - buy_2h: number; - buy_history_2h: number; - buy_2h_change_percent: number; - volume_2h: number; - volume_2h_usd: number; - volume_history_2h: number; - volume_history_2h_usd: number; - volume_2h_change_percent: number; - volume_buy_2h: number; - volume_buy_2h_usd: number; - volume_buy_history_2h: number; - volume_buy_history_2h_usd: number; - volume_buy_2h_change_percent: number; - volume_sell_2h: number; - volume_sell_2h_usd: number; - volume_sell_history_2h: number; - volume_sell_history_2h_usd: number; - volume_sell_2h_change_percent: number; - trade_4h: number; - trade_history_4h: number; - trade_4h_change_percent: number; - sell_4h: number; - sell_history_4h: number; - sell_4h_change_percent: number; - buy_4h: number; - buy_history_4h: number; - buy_4h_change_percent: number; - volume_4h: number; - volume_4h_usd: number; - volume_history_4h: number; - volume_history_4h_usd: number; - volume_4h_change_percent: number; - volume_buy_4h: number; - volume_buy_4h_usd: number; - volume_buy_history_4h: number; - volume_buy_history_4h_usd: number; - volume_buy_4h_change_percent: number; - volume_sell_4h: number; - volume_sell_4h_usd: number; - volume_sell_history_4h: number; - volume_sell_history_4h_usd: number; - volume_sell_4h_change_percent: number; - trade_8h: number; - trade_history_8h: number | null; - trade_8h_change_percent: number | null; - sell_8h: number; - sell_history_8h: number | null; - sell_8h_change_percent: number | null; - buy_8h: number; - buy_history_8h: number | null; - buy_8h_change_percent: number | null; - volume_8h: number; - volume_8h_usd: number; - volume_history_8h: number; - volume_history_8h_usd: number; - volume_8h_change_percent: number | null; - volume_buy_8h: number; - volume_buy_8h_usd: number; - volume_buy_history_8h: number; - volume_buy_history_8h_usd: number; - volume_buy_8h_change_percent: number | null; - volume_sell_8h: number; - volume_sell_8h_usd: number; - volume_sell_history_8h: number; - volume_sell_history_8h_usd: number; - volume_sell_8h_change_percent: number | null; - trade_24h: number; - trade_history_24h: number; - trade_24h_change_percent: number | null; - sell_24h: number; - sell_history_24h: number; - sell_24h_change_percent: number | null; - buy_24h: number; - buy_history_24h: number; - buy_24h_change_percent: number | null; - volume_24h: number; - volume_24h_usd: number; - volume_history_24h: number; - volume_history_24h_usd: number; - volume_24h_change_percent: number | null; - volume_buy_24h: number; - volume_buy_24h_usd: number; - volume_buy_history_24h: number; - volume_buy_history_24h_usd: number; - volume_buy_24h_change_percent: number | null; - volume_sell_24h: number; - volume_sell_24h_usd: number; - volume_sell_history_24h: number; - volume_sell_history_24h_usd: number; - volume_sell_24h_change_percent: number | null; -} - -export interface HolderData { - address: string; - balance: string; -} - -export interface ProcessedTokenData { - security: TokenSecurityData; - tradeData: TokenTradeData; - holderDistributionTrend: string; // 'increasing' | 'decreasing' | 'stable' - highValueHolders: Array<{ - holderAddress: string; - balanceUsd: string; - }>; - recentTrades: boolean; - highSupplyHoldersCount: number; - dexScreenerData: DexScreenerData; - - isDexScreenerListed: boolean; - isDexScreenerPaid: boolean; - tokenCodex: TokenCodex; -} - -export interface DexScreenerPair { - chainId: string; - dexId: string; - url: string; - pairAddress: string; - baseToken: { - address: string; - name: string; - symbol: string; - }; - quoteToken: { - address: string; - name: string; - symbol: string; - }; - priceNative: string; - priceUsd: string; - txns: { - m5: { buys: number; sells: number }; - h1: { buys: number; sells: number }; - h6: { buys: number; sells: number }; - h24: { buys: number; sells: number }; - }; - volume: { - h24: number; - h6: number; - h1: number; - m5: number; - }; - priceChange: { - m5: number; - h1: number; - h6: number; - h24: number; - }; - liquidity: { - usd: number; - base: number; - quote: number; - }; - fdv: number; - marketCap: number; - pairCreatedAt: number; - info: { - imageUrl: string; - websites: { label: string; url: string }[]; - socials: { type: string; url: string }[]; - }; - boosts: { - active: number; - }; -} - -export interface DexScreenerData { - schemaVersion: string; - pairs: DexScreenerPair[]; -} - -export interface Prices { - solana: { usd: string }; - bitcoin: { usd: string }; - ethereum: { usd: string }; -} - -export interface CalculatedBuyAmounts { - none: 0; - low: number; - medium: number; - high: number; -} From bdbc2feb71356789307525ae87ea965a5efc3dfb Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 20:42:03 +0800 Subject: [PATCH 05/58] fix: lint --- .../src/actions/createToken.ts | 7 +- .../src/actions/transfer.ts | 263 ------------------ packages/plugin-solana-agentkit/src/index.ts | 2 +- 3 files changed, 3 insertions(+), 269 deletions(-) delete mode 100644 packages/plugin-solana-agentkit/src/actions/transfer.ts diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts index a225cc1bbb..08aba878f4 100644 --- a/packages/plugin-solana-agentkit/src/actions/createToken.ts +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -22,10 +22,7 @@ export interface CreateTokenContent extends Content { initialSupply: number; } -function isCreateTokenContent( - runtime: IAgentRuntime, - content: any -): content is CreateTokenContent { +function isCreateTokenContent(content: any): content is CreateTokenContent { elizaLogger.log("Content for createToken", content); return ( typeof content.name === "string" && @@ -94,7 +91,7 @@ export default { }); // Validate transfer content - if (!isCreateTokenContent(runtime, content)) { + if (!isCreateTokenContent(content)) { elizaLogger.error("Invalid content for CREATE_TOKEN action."); if (callback) { callback({ diff --git a/packages/plugin-solana-agentkit/src/actions/transfer.ts b/packages/plugin-solana-agentkit/src/actions/transfer.ts deleted file mode 100644 index 118e2b2468..0000000000 --- a/packages/plugin-solana-agentkit/src/actions/transfer.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { - getAssociatedTokenAddressSync, - createTransferInstruction, -} from "@solana/spl-token"; -import { elizaLogger, settings } from "@elizaos/core"; - -import { - Connection, - PublicKey, - TransactionMessage, - VersionedTransaction, -} from "@solana/web3.js"; - -import { - ActionExample, - Content, - HandlerCallback, - IAgentRuntime, - Memory, - ModelClass, - State, - type Action, -} from "@elizaos/core"; -import { composeContext } from "@elizaos/core"; -import { getWalletKey } from "../keypairUtils"; -import { generateObjectDeprecated } from "@elizaos/core"; - -export interface TransferContent extends Content { - tokenAddress: string; - recipient: string; - amount: string | number; -} - -function isTransferContent( - runtime: IAgentRuntime, - content: any -): content is TransferContent { - console.log("Content for transfer", content); - return ( - typeof content.tokenAddress === "string" && - typeof content.recipient === "string" && - (typeof content.amount === "string" || - typeof content.amount === "number") - ); -} - -const transferTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. - -Example response: -\`\`\`json -{ - "tokenAddress": "BieefG47jAHCGZBxi2q87RDuHyGZyYC3vAzxpyu8pump", - "recipient": "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", - "amount": "1000" -} -\`\`\` - -{{recentMessages}} - -Given the recent messages, extract the following information about the requested token transfer: -- Token contract address -- Recipient wallet address -- Amount to transfer - -Respond with a JSON markdown block containing only the extracted values.`; - -export default { - name: "SEND_TOKEN", - similes: [ - "TRANSFER_TOKEN", - "TRANSFER_TOKENS", - "SEND_TOKENS", - "SEND_SOL", - "PAY", - ], - validate: async (runtime: IAgentRuntime, message: Memory) => { - console.log("Validating transfer from user:", message.userId); - //add custom validate logic here - /* - const adminIds = runtime.getSetting("ADMIN_USER_IDS")?.split(",") || []; - //console.log("Admin IDs from settings:", adminIds); - - const isAdmin = adminIds.includes(message.userId); - - if (isAdmin) { - //console.log(`Authorized transfer from user: ${message.userId}`); - return true; - } - else - { - //console.log(`Unauthorized transfer attempt from user: ${message.userId}`); - return false; - } - */ - return false; - }, - description: "Transfer tokens from the agent's wallet to another address", - handler: async ( - runtime: IAgentRuntime, - message: Memory, - state: State, - _options: { [key: string]: unknown }, - callback?: HandlerCallback - ): Promise => { - elizaLogger.log("Starting SEND_TOKEN handler..."); - - // Initialize or update state - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } - - // Compose transfer context - const transferContext = composeContext({ - state, - template: transferTemplate, - }); - - // Generate transfer content - const content = await generateObjectDeprecated({ - runtime, - context: transferContext, - modelClass: ModelClass.LARGE, - }); - - // Validate transfer content - if (!isTransferContent(runtime, content)) { - console.error("Invalid content for TRANSFER_TOKEN action."); - if (callback) { - callback({ - text: "Unable to process transfer request. Invalid content provided.", - content: { error: "Invalid transfer content" }, - }); - } - return false; - } - - try { - const { keypair: senderKeypair } = await getWalletKey( - runtime, - true - ); - - const connection = new Connection(settings.RPC_URL!); - - const mintPubkey = new PublicKey(content.tokenAddress); - const recipientPubkey = new PublicKey(content.recipient); - - // Get decimals (simplest way) - const mintInfo = await connection.getParsedAccountInfo(mintPubkey); - const decimals = - (mintInfo.value?.data as any)?.parsed?.info?.decimals ?? 9; - - // Adjust amount with decimals - const adjustedAmount = BigInt( - Number(content.amount) * Math.pow(10, decimals) - ); - console.log( - `Transferring: ${content.amount} tokens (${adjustedAmount} base units)` - ); - - // Rest of the existing working code... - const senderATA = getAssociatedTokenAddressSync( - mintPubkey, - senderKeypair.publicKey - ); - const recipientATA = getAssociatedTokenAddressSync( - mintPubkey, - recipientPubkey - ); - - const instructions = []; - - const recipientATAInfo = - await connection.getAccountInfo(recipientATA); - if (!recipientATAInfo) { - const { createAssociatedTokenAccountInstruction } = - await import("@solana/spl-token"); - instructions.push( - createAssociatedTokenAccountInstruction( - senderKeypair.publicKey, - recipientATA, - recipientPubkey, - mintPubkey - ) - ); - } - - instructions.push( - createTransferInstruction( - senderATA, - recipientATA, - senderKeypair.publicKey, - adjustedAmount - ) - ); - - // Create and sign versioned transaction - const messageV0 = new TransactionMessage({ - payerKey: senderKeypair.publicKey, - recentBlockhash: (await connection.getLatestBlockhash()) - .blockhash, - instructions, - }).compileToV0Message(); - - const transaction = new VersionedTransaction(messageV0); - transaction.sign([senderKeypair]); - - // Send transaction - const signature = await connection.sendTransaction(transaction); - - console.log("Transfer successful:", signature); - - if (callback) { - callback({ - text: `Successfully transferred ${content.amount} tokens to ${content.recipient}\nTransaction: ${signature}`, - content: { - success: true, - signature, - amount: content.amount, - recipient: content.recipient, - }, - }); - } - - return true; - } catch (error) { - console.error("Error during token transfer:", error); - if (callback) { - callback({ - text: `Error transferring tokens: ${error.message}`, - content: { error: error.message }, - }); - } - return false; - } - }, - - examples: [ - [ - { - user: "{{user1}}", - content: { - text: "Send 69 EZSIS BieefG47jAHCGZBxi2q87RDuHyGZyYC3vAzxpyu8pump to 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa", - }, - }, - { - user: "{{user2}}", - content: { - text: "I'll send 69 EZSIS tokens now...", - action: "SEND_TOKEN", - }, - }, - { - user: "{{user2}}", - content: { - text: "Successfully sent 69 EZSIS tokens to 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa\nTransaction: 5KtPn3DXXzHkb7VAVHZGwXJQqww39ASnrf7YkyJoF2qAGEpBEEGvRHLnnTG8ZVwKqNHMqSckWVGnsQAgfH5pbxEb", - }, - }, - ], - ] as ActionExample[][], -} as Action; diff --git a/packages/plugin-solana-agentkit/src/index.ts b/packages/plugin-solana-agentkit/src/index.ts index 3a7749a528..ae80c66c74 100644 --- a/packages/plugin-solana-agentkit/src/index.ts +++ b/packages/plugin-solana-agentkit/src/index.ts @@ -6,7 +6,7 @@ export const solanaAgentkitPlguin: Plugin = { description: "Solana Plugin with solana agent kit for Eliza", actions: [createToken], evaluators: [], - providers: [,], + providers: [], }; export default solanaAgentkitPlguin; From 7b8fd8d9101c627f23eb7bd140600b3d566b6275 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 20:46:46 +0800 Subject: [PATCH 06/58] fix: typo --- packages/plugin-solana-agentkit/src/actions/createToken.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts index 08aba878f4..f3b718820a 100644 --- a/packages/plugin-solana-agentkit/src/actions/createToken.ts +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -151,7 +151,7 @@ export default { { user: "{{user2}}", content: { - text: "I'll creaete token now...", + text: "I'll create token now...", action: "CREATE_TOKEN", }, }, From 25b484a506ab56818ae4f517a39de82e7c61c52e Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 22 Dec 2024 21:14:48 +0800 Subject: [PATCH 07/58] fix: more log --- .../plugin-solana-agentkit/src/actions/createToken.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts index f3b718820a..b0720348d5 100644 --- a/packages/plugin-solana-agentkit/src/actions/createToken.ts +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -112,19 +112,21 @@ export default { openAIKey ); try { - const deployedAddress = solanaAgentKit.deployToken( + const deployedAddress = await solanaAgentKit.deployToken( content.name, content.uri, content.symbol, - content.decimals, - content.initialSupply + content.decimals + // content.initialSupply comment out this cause the sdk has some issue with this parameter ); elizaLogger.log("Create successful: ", deployedAddress); + elizaLogger.log(deployedAddress); if (callback) { callback({ text: `Successfully create token ${content.name}`, content: { success: true, + deployedAddress, }, }); } From b13a6f8067cfb9d0fbb73b7c6a3d00539a347e23 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 26 Dec 2024 13:47:25 +0100 Subject: [PATCH 08/58] feat: Make templates in composeContext dynamic --- docs/api/functions/composeContext.md | 8 +- docs/docs/api/functions/composeContext.md | 20 +++-- packages/client-twitter/src/post.ts | 7 +- packages/core/src/context.ts | 21 ++++-- packages/core/src/tests/context.test.ts | 92 ++++++++++++++++++++++- packages/core/src/types.ts | 50 ++++++------ 6 files changed, 153 insertions(+), 45 deletions(-) diff --git a/docs/api/functions/composeContext.md b/docs/api/functions/composeContext.md index 4d3ae6ea39..eb2fbcf7d8 100644 --- a/docs/api/functions/composeContext.md +++ b/docs/api/functions/composeContext.md @@ -2,7 +2,7 @@ # Function: composeContext() -> **composeContext**(`params`): `any` +> **composeContext**(`params`): `string` Composes a context string by replacing placeholders in a template with corresponding values from the state. @@ -22,9 +22,9 @@ The parameters for composing the context. The state object containing values to replace the placeholders in the template. -• **params.template**: `string` +• **params.template**: `string` | `Function` -The template string containing placeholders to be replaced with state values. +The template string or function returning a string containing placeholders to be replaced with state values. • **params.templatingEngine?**: `"handlebars"` @@ -32,7 +32,7 @@ The templating engine to use for compiling and evaluating the template (optional ## Returns -`any` +`string` The composed context string with placeholders replaced by corresponding state values. diff --git a/docs/docs/api/functions/composeContext.md b/docs/docs/api/functions/composeContext.md index 7ff2652bc2..cde69d271c 100644 --- a/docs/docs/api/functions/composeContext.md +++ b/docs/docs/api/functions/composeContext.md @@ -10,13 +10,13 @@ Composes a context string by replacing placeholders in a template with values fr An object containing the following properties: -- **state**: `State` +- **state**: `State` The state object containing key-value pairs for replacing placeholders in the template. -- **template**: `string` - A string containing placeholders in the format `{{placeholder}}`. +- **template**: `string | Function` + A string or function returning a string containing placeholders in the format `{{placeholder}}`. -- **templatingEngine**: `"handlebars" | undefined` *(optional)* +- **templatingEngine**: `"handlebars" | undefined` _(optional)_ The templating engine to use. If set to `"handlebars"`, the Handlebars engine is used for template compilation. Defaults to `undefined` (simple string replacement). ## Returns @@ -38,7 +38,11 @@ const contextSimple = composeContext({ state, template }); // Output: "Hello, Alice! You are 30 years old." // Handlebars templating -const contextHandlebars = composeContext({ state, template, templatingEngine: 'handlebars' }); +const contextHandlebars = composeContext({ + state, + template, + templatingEngine: "handlebars", +}); // Output: "Hello, Alice! You are 30 years old." ``` @@ -47,7 +51,7 @@ const contextHandlebars = composeContext({ state, template, templatingEngine: 'h ```javascript const advancedTemplate = ` {{#if userAge}} - Hello, {{userName}}! + Hello, {{userName}}! {{#if (gt userAge 18)}}You are an adult.{{else}}You are a minor.{{/if}} {{else}} Hello! We don't know your age. @@ -66,14 +70,14 @@ const advancedTemplate = ` const advancedState = { userName: "Alice", userAge: 30, - favoriteColors: ["blue", "green", "red"] + favoriteColors: ["blue", "green", "red"], }; // Composing the context with Handlebars const advancedContextHandlebars = composeContext({ state: advancedState, template: advancedTemplate, - templatingEngine: 'handlebars' + templatingEngine: "handlebars", }); // Output: // Hello, Alice! diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index 03c332a18f..08831578fb 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -7,6 +7,7 @@ import { ModelClass, stringToUuid, parseBooleanFromText, + TemplateType, } from "@elizaos/core"; import { elizaLogger } from "@elizaos/core"; import { ClientBase } from "./base.ts"; @@ -164,8 +165,8 @@ export class TwitterPostClient { this.runtime.getSetting("POST_IMMEDIATELY") !== "" ) { // Retrieve setting, default to false if not set or if the value is not "true" - postImmediately = this.runtime.getSetting("POST_IMMEDIATELY") === "true" || false; - + postImmediately = + this.runtime.getSetting("POST_IMMEDIATELY") === "true" || false; } if (postImmediately) { @@ -379,7 +380,7 @@ export class TwitterPostClient { private async generateTweetContent( tweetState: any, options?: { - template?: string; + template?: TemplateType; context?: string; } ): Promise { diff --git a/packages/core/src/context.ts b/packages/core/src/context.ts index a682e6794c..059e302d62 100644 --- a/packages/core/src/context.ts +++ b/packages/core/src/context.ts @@ -1,5 +1,5 @@ import handlebars from "handlebars"; -import { type State } from "./types.ts"; +import { type State, type TemplateType } from "./types.ts"; import { names, uniqueNamesGenerator } from "unique-names-generator"; /** @@ -13,7 +13,7 @@ import { names, uniqueNamesGenerator } from "unique-names-generator"; * * @param {Object} params - The parameters for composing the context. * @param {State} params.state - The state object containing values to replace the placeholders in the template. - * @param {string} params.template - The template string containing placeholders to be replaced with state values. + * @param {TemplateType} params.template - The template string or function containing placeholders to be replaced with state values. * @param {"handlebars" | undefined} [params.templatingEngine] - The templating engine to use for compiling and evaluating the template (optional, default: `undefined`). * @returns {string} The composed context string with placeholders replaced by corresponding state values. * @@ -25,23 +25,34 @@ import { names, uniqueNamesGenerator } from "unique-names-generator"; * // Composing the context with simple string replacement will result in: * // "Hello, Alice! You are 30 years old." * const contextSimple = composeContext({ state, template }); + * + * // Using composeContext with a template function for dynamic template + * const template = ({ state }) => { + * const tone = Math.random() > 0.5 ? "kind" : "rude"; + * return `Hello, {{userName}}! You are {{userAge}} years old. Be ${tone}`; + * }; + * const contextSimple = composeContext({ state, template }); */ + export const composeContext = ({ state, template, templatingEngine, }: { state: State; - template: string; + template: TemplateType; templatingEngine?: "handlebars"; }) => { + const templateStr = + typeof template === "function" ? template({ state }) : template; + if (templatingEngine === "handlebars") { - const templateFunction = handlebars.compile(template); + const templateFunction = handlebars.compile(templateStr); return templateFunction(state); } // @ts-expect-error match isn't working as expected - const out = template.replace(/{{\w+}}/g, (match) => { + const out = templateStr.replace(/{{\w+}}/g, (match) => { const key = match.replace(/{{|}}/g, ""); return state[key] ?? ""; }); diff --git a/packages/core/src/tests/context.test.ts b/packages/core/src/tests/context.test.ts index 6bf391282b..3c3bc978f9 100644 --- a/packages/core/src/tests/context.test.ts +++ b/packages/core/src/tests/context.test.ts @@ -70,6 +70,96 @@ describe("composeContext", () => { }); }); + describe("dynamic templates", () => { + it("should handle function templates", () => { + const state: State = { + ...baseState, + userName: "Alice", + userAge: 30, + }; + const template = () => { + return "Hello, {{userName}}! You are {{userAge}} years old."; + }; + + const result = composeContext({ state, template }); + + expect(result).toBe("Hello, Alice! You are 30 years old."); + }); + + it("should handle function templates with conditional logic", () => { + const state: State = { + ...baseState, + userName: "Alice", + userAge: 30, + }; + const isEdgy = true; + const template = () => { + if (isEdgy) { + return "Hello, {{userName}}! You are {{userAge}} years old... whatever"; + } + + return `Hello, {{userName}}! You are {{userAge}} years old`; + }; + + const result = composeContext({ state, template }); + + expect(result).toBe( + "Hello, Alice! You are 30 years old... whatever" + ); + }); + + it("should handle function templates with conditional logic depending on state", () => { + const template = ({ state }: { state: State }) => { + if (state.userName) { + return `Hello, {{userName}}! You are {{userAge}} years old.`; + } + + return `Hello, anon! You are {{userAge}} years old.`; + }; + + const result = composeContext({ + state: { + ...baseState, + userName: "Alice", + userAge: 30, + }, + template, + }); + + const resultWithoutUsername = composeContext({ + state: { + ...baseState, + userAge: 30, + }, + template, + }); + + expect(result).toBe("Hello, Alice! You are 30 years old."); + expect(resultWithoutUsername).toBe( + "Hello, anon! You are 30 years old." + ); + }); + + it("should handle function templates with handlebars templating engine", () => { + const state: State = { + ...baseState, + userName: "Alice", + userAge: 30, + }; + const template = () => { + return `{{#if userAge}}Hello, {{userName}}!{{else}}Hi there!{{/if}}`; + }; + + const result = composeContext({ + state, + template, + templatingEngine: "handlebars", + }); + + expect(result).toBe("Hello, Alice!"); + }); + }); + // Test Handlebars templating describe("handlebars templating", () => { it("should process basic handlebars template", () => { @@ -160,7 +250,7 @@ describe("composeContext", () => { }); it("should handle missing values in handlebars template", () => { - const state = {...baseState} + const state = { ...baseState }; const template = "Hello, {{userName}}!"; const result = composeContext({ diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 6786bb99f9..a8aa3523e0 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -634,6 +634,8 @@ export interface ModelConfiguration { maxInputTokens?: number; } +export type TemplateType = string | ((options: { state: State }) => string); + /** * Configuration for an agent character */ @@ -661,30 +663,30 @@ export type Character = { /** Optional prompt templates */ templates?: { - goalsTemplate?: string; - factsTemplate?: string; - messageHandlerTemplate?: string; - shouldRespondTemplate?: string; - continueMessageHandlerTemplate?: string; - evaluationTemplate?: string; - twitterSearchTemplate?: string; - twitterActionTemplate?: string; - twitterPostTemplate?: string; - twitterMessageHandlerTemplate?: string; - twitterShouldRespondTemplate?: string; - farcasterPostTemplate?: string; - lensPostTemplate?: string; - farcasterMessageHandlerTemplate?: string; - lensMessageHandlerTemplate?: string; - farcasterShouldRespondTemplate?: string; - lensShouldRespondTemplate?: string; - telegramMessageHandlerTemplate?: string; - telegramShouldRespondTemplate?: string; - discordVoiceHandlerTemplate?: string; - discordShouldRespondTemplate?: string; - discordMessageHandlerTemplate?: string; - slackMessageHandlerTemplate?: string; - slackShouldRespondTemplate?: string; + goalsTemplate?: TemplateType; + factsTemplate?: TemplateType; + messageHandlerTemplate?: TemplateType; + shouldRespondTemplate?: TemplateType; + continueMessageHandlerTemplate?: TemplateType; + evaluationTemplate?: TemplateType; + twitterSearchTemplate?: TemplateType; + twitterActionTemplate?: TemplateType; + twitterPostTemplate?: TemplateType; + twitterMessageHandlerTemplate?: TemplateType; + twitterShouldRespondTemplate?: TemplateType; + farcasterPostTemplate?: TemplateType; + lensPostTemplate?: TemplateType; + farcasterMessageHandlerTemplate?: TemplateType; + lensMessageHandlerTemplate?: TemplateType; + farcasterShouldRespondTemplate?: TemplateType; + lensShouldRespondTemplate?: TemplateType; + telegramMessageHandlerTemplate?: TemplateType; + telegramShouldRespondTemplate?: TemplateType; + discordVoiceHandlerTemplate?: TemplateType; + discordShouldRespondTemplate?: TemplateType; + discordMessageHandlerTemplate?: TemplateType; + slackMessageHandlerTemplate?: TemplateType; + slackShouldRespondTemplate?: TemplateType; }; /** Character biography */ From 60bf5836d4fcff5b120ce5a22e6d7b45c843e9ae Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 26 Dec 2024 15:17:57 +0100 Subject: [PATCH 09/58] correct import of plugin-cronoszkevm --- agent/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/index.ts b/agent/src/index.ts index accb825735..8ee5d362b4 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -56,7 +56,7 @@ import { suiPlugin } from "@elizaos/plugin-sui"; import { TEEMode, teePlugin } from "@elizaos/plugin-tee"; import { tonPlugin } from "@elizaos/plugin-ton"; import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; -import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkEVM"; +import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; import { abstractPlugin } from "@elizaos/plugin-abstract"; import Database from "better-sqlite3"; import fs from "fs"; From a4bee670d0d839f81e5ca4d8de5d7d7bd81bb9ab Mon Sep 17 00:00:00 2001 From: sinecose Date: Fri, 3 Jan 2025 16:35:15 +0800 Subject: [PATCH 10/58] feat(plugin-cronoszkevm): rm not used imports --- packages/plugin-cronoszkevm/src/actions/transfer.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/plugin-cronoszkevm/src/actions/transfer.ts b/packages/plugin-cronoszkevm/src/actions/transfer.ts index f91dcea880..2007810ea8 100644 --- a/packages/plugin-cronoszkevm/src/actions/transfer.ts +++ b/packages/plugin-cronoszkevm/src/actions/transfer.ts @@ -14,12 +14,7 @@ import { import { validateCronosZkevmConfig } from "../enviroment"; import { Web3 } from "web3"; -import { - ZKsyncPlugin, - ZKsyncWallet, - types, - Web3ZKsyncL2, -} from "web3-plugin-zksync"; +import { ZKsyncPlugin, Web3ZKsyncL2 } from "web3-plugin-zksync"; export interface TransferContent extends Content { tokenAddress: string; @@ -87,7 +82,7 @@ export default { "PAY_ON_CRONOSZKEVM", "PAY_ON_CRONOSZK", ], - validate: async (runtime: IAgentRuntime, message: Memory) => { + validate: async (runtime: IAgentRuntime, _message: Memory) => { await validateCronosZkevmConfig(runtime); return true; }, From daed957a1391681e6b1d11a123c57bdc221e18d9 Mon Sep 17 00:00:00 2001 From: shaungliang abc Date: Sat, 4 Jan 2025 17:23:06 +0800 Subject: [PATCH 11/58] fix --- packages/core/src/types.ts | 1 + packages/plugin-goplus/.npmignore | 6 + packages/plugin-goplus/eslint.config.mjs | 3 + packages/plugin-goplus/package.json | 21 ++ packages/plugin-goplus/src/index.ts | 17 ++ .../plugin-goplus/src/lib/GoPlusManage.ts | 130 +++++++++++ .../src/services/GoplusSecurityService.ts | 98 ++++++++ packages/plugin-goplus/src/templates/index.ts | 209 ++++++++++++++++++ packages/plugin-goplus/tsconfig.json | 11 + packages/plugin-goplus/tsup.config.ts | 10 + 10 files changed, 506 insertions(+) create mode 100644 packages/plugin-goplus/.npmignore create mode 100644 packages/plugin-goplus/eslint.config.mjs create mode 100644 packages/plugin-goplus/package.json create mode 100644 packages/plugin-goplus/src/index.ts create mode 100644 packages/plugin-goplus/src/lib/GoPlusManage.ts create mode 100644 packages/plugin-goplus/src/services/GoplusSecurityService.ts create mode 100644 packages/plugin-goplus/src/templates/index.ts create mode 100644 packages/plugin-goplus/tsconfig.json create mode 100644 packages/plugin-goplus/tsup.config.ts diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c07d3226b9..2ceb362100 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1295,6 +1295,7 @@ export enum ServiceType { AWS_S3 = "aws_s3", BUTTPLUG = "buttplug", SLACK = "slack", + GOPLUS_SECURITY = "goplus_security", } export enum LoggingLevel { diff --git a/packages/plugin-goplus/.npmignore b/packages/plugin-goplus/.npmignore new file mode 100644 index 0000000000..078562ecea --- /dev/null +++ b/packages/plugin-goplus/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-goplus/eslint.config.mjs b/packages/plugin-goplus/eslint.config.mjs new file mode 100644 index 0000000000..92fe5bbebe --- /dev/null +++ b/packages/plugin-goplus/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-goplus/package.json b/packages/plugin-goplus/package.json new file mode 100644 index 0000000000..6b9e66ccba --- /dev/null +++ b/packages/plugin-goplus/package.json @@ -0,0 +1,21 @@ +{ + "name": "@elizaos/plugin-goplus", + "version": "0.1.7-alpha.2", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "tsup": "^8.3.5", + "ws": "^8.18.0" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsx watch src/index.ts", + "lint": "eslint --fix --cache ." + }, + "devDependencies": { + "@types/ws": "^8.5.13", + "tsx": "^4.19.2" + } +} diff --git a/packages/plugin-goplus/src/index.ts b/packages/plugin-goplus/src/index.ts new file mode 100644 index 0000000000..d64fb3e905 --- /dev/null +++ b/packages/plugin-goplus/src/index.ts @@ -0,0 +1,17 @@ +import { Plugin } from "@elizaos/core"; +import GoplusSecurityService from "./services/GoplusSecurityService"; + +export * from "./services/GoplusSecurityService"; + + +export const goplusPlugin: Plugin = { + name: "goplus", + description: + "goplus Plugin for Eliza - Enables WebSocket communication for AI-driven market insights", + actions: [], + evaluators: [], + providers: [], + services: [new GoplusSecurityService()], +}; + +export default goplusPlugin; diff --git a/packages/plugin-goplus/src/lib/GoPlusManage.ts b/packages/plugin-goplus/src/lib/GoPlusManage.ts new file mode 100644 index 0000000000..1406e167ff --- /dev/null +++ b/packages/plugin-goplus/src/lib/GoPlusManage.ts @@ -0,0 +1,130 @@ + + +export const GoPlusType = { + EVMTOKEN_SECURITY_CHECK: "EVMTOKEN_SECURITY_CHECK", + SOLTOKEN_SECURITY_CHECK: "SOLTOKEN_SECURITY_CHECK", + SUITOKEN_SECURITY_CHECK: "SUITOKEN_SECURITY_CHECK", + RUGPULL_SECURITY_CHECK: "RUGPULL_SECURITY_CHECK", + NFT_SECURITY_CHECK: "NFT_SECURITY_CHECK", + ADRESS_SECURITY_CHECK: "ADRESS_SECURITY_CHECK", + APPROVAL_SECURITY_CHECK: "APPROVAL_SECURITY_CHECK", + ACCOUNT_ERC20_SECURITY_CHECK: "ACCOUNT_ERC20_SECURITY_CHECK", + ACCOUNT_ERC721_SECURITY_CHECK: "ACCOUNT_ERC721_SECURITY_CHECK", + ACCOUNT_ERC1155_SECURITY_CHECK: "ACCOUNT_ERC1155_SECURITY_CHECK", + SIGNATURE_SECURITY_CHECK: "SIGNATURE_SECURITY_CHECK", + URL_SECURITY_CHECK: "URL_SECURITY_CHECK", +} + +export type GoPlusType = (typeof GoPlusType)[keyof typeof GoPlusType] + +export type GoPlusParamType = { + "type": GoPlusType, + "network"?: string, + "token"?: string, + "contract"?: string, + "wallet"?: string, + "url"?: string, + "data"?: string, +} + +export class GoPlusManage { + private apiKey: string; + + constructor(apiKey: string = null) { + this.apiKey = apiKey; + } + + async requestGet(api: string) { + const myHeaders = new Headers(); + if (this.apiKey) { + myHeaders.append("Authorization", this.apiKey); + } + const url = `https://api.gopluslabs.io/${api}` + const res = await fetch(url, { + method: "GET", + headers: myHeaders, + redirect: "follow" + }) + + return await res.json(); + } + + async tokenSecurity(chainId: string, address: string) { + const api = `api/v1/token_security/${chainId}?contract_addresses=${address}`; + return await this.requestGet(api) + } + + async rugpullDetection(chainId: string, address: string) { + const api = `api/v1/rugpull_detecting/${chainId}?contract_addresses=${address}`; + return await this.requestGet(api) + } + + async solanaTokenSecurityUsingGET(address: string) { + const api = `api/v1/solana/token_security?contract_addresses=${address}`; + return await this.requestGet(api) + } + + async suiTokenSecurityUsingGET(address: string) { + const api = `api/v1/sui/token_security?contract_addresses=${address}`; + return await this.requestGet(api) + } + + async nftSecurity(chainId: string, address: string) { + const api = `api/v1/nft_security/${chainId}?contract_addresses=${address}`; + return await this.requestGet(api) + } + + async addressSecurity(address: string) { + const api = `api/v1/address_security/${address}`; + return await this.requestGet(api) + } + + async approvalSecurity(chainId: string, contract: string) { + const api = `api/v1/approval_security/${chainId}?contract_addresses=${contract}`; + return await this.requestGet(api) + } + + async erc20ApprovalSecurity(chainId: string, wallet: string) { + const api = `api/v2/token_approval_security/${chainId}?addresses=${wallet}`; + return await this.requestGet(api) + } + + async erc721ApprovalSecurity(chainId: string, wallet: string) { + const api = `api/v2/nft721_approval_security/${chainId}?addresses=${wallet}`; + return await this.requestGet(api) + } + + async erc1155ApprovalSecurity(chainId: string, wallet: string) { + const api = `api/v2/nft1155_approval_security/${chainId}?addresses=${wallet}`; + return await this.requestGet(api) + } + + async inputDecode(chainId: string, data: string) { + const body = JSON.stringify({ + chain_id: chainId, + data: data, + }) + const res = await fetch("https://api.gopluslabs.io/api/v1/abi/input_decode", { + "headers": { + "accept": "*/*", + "accept-language": "en,zh-CN;q=0.9,zh;q=0.8", + "content-type": "application/json" + }, + "body": body, + "method": "POST" + }); + return await res.json(); + } + + async dappSecurityAndPhishingSite(url: string) { + const api = `api/v1/dapp_security?url=${url}`; + const data1 = await this.requestGet(api) + + const api2 = `api/v1/phishing_site?url=${url}`; + const data2 = await this.requestGet(api2) + return { + data1, + data2 + } + } +} \ No newline at end of file diff --git a/packages/plugin-goplus/src/services/GoplusSecurityService.ts b/packages/plugin-goplus/src/services/GoplusSecurityService.ts new file mode 100644 index 0000000000..e23151e2be --- /dev/null +++ b/packages/plugin-goplus/src/services/GoplusSecurityService.ts @@ -0,0 +1,98 @@ +import { IAgentRuntime, ModelClass, Service, ServiceType, elizaLogger, generateObjectDeprecated, generateText } from "@elizaos/core"; +import { GoPlusManage, GoPlusParamType, GoPlusType } from "../lib/GoPlusManage"; +import { requestPrompt, responsePrompt } from "../templates"; + +export interface IGoplusSecurityService extends Service { + check(text: string): Promise; +} + +export class GoplusSecurityService extends Service implements IGoplusSecurityService { + private apiKey: string; + private runtime: IAgentRuntime; + getInstance(): GoplusSecurityService { + return this; + } + static get serviceType() { + return ServiceType.GOPLUS_SECURITY; + } + + initialize(runtime: IAgentRuntime): Promise { + this.runtime = runtime; + this.apiKey = runtime.getSetting("GOPLUS_API_KEY"); + return; + } + + + /** + * Connect to WebSocket and send a message + */ + async check(text: string): Promise { + try { + elizaLogger.log("check input text", text); + const obj = await generateObjectDeprecated({ + runtime: this.runtime, + context: requestPrompt(text), + modelClass: ModelClass.SMALL, // gpt-4o-mini + }) as GoPlusParamType; + + elizaLogger.log("check generateObjectDeprecated text", obj); + + const goPlusManage = new GoPlusManage(this.apiKey) + let checkResult: any; + switch(obj.type) { + case GoPlusType.EVMTOKEN_SECURITY_CHECK: + checkResult = await goPlusManage.tokenSecurity(obj.network, obj.token); + break; + case GoPlusType.SOLTOKEN_SECURITY_CHECK: + checkResult = await goPlusManage.solanaTokenSecurityUsingGET(obj.token); + break; + case GoPlusType.SUITOKEN_SECURITY_CHECK: + checkResult = await goPlusManage.suiTokenSecurityUsingGET(obj.token); + break; + case GoPlusType.RUGPULL_SECURITY_CHECK: + checkResult = await goPlusManage.rugpullDetection(obj.network, obj.contract); + break; + case GoPlusType.NFT_SECURITY_CHECK: + checkResult = await goPlusManage.nftSecurity(obj.network, obj.token); + break; + case GoPlusType.ADRESS_SECURITY_CHECK: + checkResult = await goPlusManage.addressSecurity(obj.wallet); + break; + case GoPlusType.APPROVAL_SECURITY_CHECK: + checkResult = await goPlusManage.approvalSecurity(obj.network, obj.contract); + break; + case GoPlusType.ACCOUNT_ERC20_SECURITY_CHECK: + checkResult = await goPlusManage.erc20ApprovalSecurity(obj.network, obj.wallet); + break; + case GoPlusType.ACCOUNT_ERC721_SECURITY_CHECK: + checkResult = await goPlusManage.erc721ApprovalSecurity(obj.network, obj.wallet); + break; + case GoPlusType.ACCOUNT_ERC1155_SECURITY_CHECK: + checkResult = await goPlusManage.erc1155ApprovalSecurity(obj.network, obj.wallet); + break; + case GoPlusType.SIGNATURE_SECURITY_CHECK: + checkResult = await goPlusManage.inputDecode(obj.network, obj.data); + break; + case GoPlusType.URL_SECURITY_CHECK: + checkResult = await goPlusManage.dappSecurityAndPhishingSite(obj.url); + break; + default: + throw new Error("type is invaild") + } + + elizaLogger.log("checkResult text", checkResult); + const checkResponse = await generateText({ + runtime: this.runtime, + context: responsePrompt(JSON.stringify(checkResult), text), + modelClass: ModelClass.SMALL, + }); + elizaLogger.log("checkResponse text", checkResponse); + return checkResponse + } catch (e) { + elizaLogger.error(e); + return "error"; + } + } +} + +export default GoplusSecurityService; diff --git a/packages/plugin-goplus/src/templates/index.ts b/packages/plugin-goplus/src/templates/index.ts new file mode 100644 index 0000000000..6886211d7f --- /dev/null +++ b/packages/plugin-goplus/src/templates/index.ts @@ -0,0 +1,209 @@ +export const requestPrompt = (text:string) => `You are a security action detector for blockchain interactions. Your task is to analyze the user's input text and determine which security checks are needed. + +Text to analyze:""" +${text} +""" +If the user is not sure which network the sent address belongs to, then according to the following logic initially determine which network the user sends the address belongs to. + +Detection Logic: +1. First check if address starts with "0x": + - If yes: + - If length is 42 -> EVM address + - If the address has a non-standard suffix (e.g., " ::s::S "), you may treat the base address (without the suffix) as the -> SUI address. , but the full address including the suffix should be placed in the "token" field. + - If no: + - If length is 44 and starts with letter -> Solana address + +2. If none of the above patterns match: + - -> EVM address +3. If detection is EVM address: + - -> EVM address + +Networks format +EVM: 0x26e550ac11b26f78a04489d5f20f24e3559f7dd9 +Solana: 9DHe3pycTuymFk4H4bbPoAJ4hQrr2kaLDF6J6aAKpump +SUI: 0xea65bb5a79ff34ca83e2995f9ff6edd0887b08da9b45bf2e31f930d3efb82866::s::S + +After determining which action to use, please reply in the json format below the action. + +Available actions: +- [EVMTOKEN_SECURITY_CHECK]: For checking ERC20 token contract security + Description: Security assessment for tokens on EVM-compatible chains (like Ethereum, BSC), including contract risks, permission configurations, transaction mechanisms + Keywords: EVM token, ETH token, BEP20, smart contract, ERC20 security, on-chain token + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "EVMTOKEN_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, ETHW:10001, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Base:8453, Tron:tron, Scroll:534352, opBNB:204, Mantle:5000, ZKFair:42766, Blast:81457, Manta Pacific:169, Berachain Artio Testnet:80085, Merlin:4200, Bitlayer Mainnet:200901, zkLink Nova:810180, X Layer Mainnet:196) +"token": "" , +} +\`\`\` + + +- [SOLTOKEN_SECURITY_CHECK]: For checking SPL token contract security + Description: Security audit for Solana-based tokens, analyzing program authority settings, account states, transfer restrictions and other security factors + Keywords: Solana token, SOL token, SPL token, Solana security, SOL contract + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "SOLTOKEN_SECURITY_CHECK" +"token": "" , +} +\`\`\` + + +- [SUITOKEN_SECURITY_CHECK]: For checking Sui token contract security + Description: Security inspection for tokens on SUI blockchain, examining token contract permissions, transaction restrictions, minting mechanisms and other security configurations + Keywords: SUI token, SUI coins, MOVE token, SUI contract, SUI security + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "SUITOKEN_SECURITY_CHECK" +"token": "" , +} +\`\`\` + + +- [RUGPULL_SECURITY_CHECK]: + Description: Detection of potential rugpull risks in tokens/projects, including contract permissions, liquidity locks, team holdings and other risk factors + Keywords: rugpull risk, token security, project reliability, contract risk, liquidity, team wallet + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "RUGPULL_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, BSC:56) +"contract": "" | null, +} +\`\`\` + + +- [NFT_SECURITY_CHECK] + Description: Security analysis of NFT project smart contracts, including minting mechanisms, trading restrictions, permission settings + Keywords: NFT security, digital collectibles, minting risk, NFT trading, NFT contract + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "NFT_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Base:8453, Mantle:5000) +"token": "" | null, +} +\`\`\` + + +- [ADRESS_SECURITY_CHECK] + Description: Analysis of specific address security status, detecting known malicious addresses, scam addresses or high-risk addresses + Keywords: wallet security, malicious address, scam address, blacklist + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "ADRESS_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Tron:tron, Scroll:534352, opBNB:204, Base:8453, Solana:solana) +"wallet": "" | null, +} +\`\`\` + + +- [APPROVAL_SECURITY_CHECK] + Description: Examination of smart contract approval settings, evaluating risk levels of third-party authorizations + Keywords: approval check, contract authorization, spending approval, approval risk + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "APPROVAL_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, BSC: 56, OKC: 66, Heco: 128, Polygon: 137, Fantom:250, Arbitrum: 42161, Avalanche: 43114) +"contract": "" | null, +} +\`\`\` + + +- [ACCOUNT_ERC20_SECURITY_CHECK] + Description: Security assessment of account-related ERC20 token transactions and holdings + Keywords: ERC20, token account, token security, account detection + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "ACCOUNT_ERC20_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Base:8453, Mantle:5000) +"wallet": "" | null, +} +\`\`\` + + +- [ACCOUNT_ERC721_SECURITY_CHECK] + Description: Security analysis of account's ERC721 NFT assets + Keywords: ERC721, NFT account, NFT assets, collectibles security + Respond with a JSON markdown block containing only the extracted values: +\`\`\`json +{ +"type": "ACCOUNT_ERC721_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Base:8453, Mantle:5000) +"wallet": "" | null, +} +\`\`\` + + +- [ACCOUNT_ERC1155_SECURITY_CHECK] + Description: Security evaluation of account's ERC1155 multi-token standard assets + Keywords: ERC1155, multi-token, hybrid assets, account security + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "ACCOUNT_ERC1155_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum:1, Optimism:10, Cronos:25, BSC:56, Gnosis:100, HECO:128, Polygon:137, Fantom:250, KCC:321, zkSync Era:324, FON:201022, Arbitrum:42161, Avalanche:43114, Linea Mainnet:59144, Base:8453, Mantle:5000) +"wallet": "" | null, +} +\`\`\` + + +- [SIGNATURE_SECURITY_CHECK] + Description: Verification of signature security, preventing signature fraud risks + Keywords: signature verification, message signing, signature risk, signature fraud + Respond with a JSON markdown block containing only the extracted values: + +\`\`\`json +{ +"type": "SIGNATURE_SECURITY_CHECK" +"network": "1", //default: 1 (Ethereum: 1, Cronos:25, BSC: 56, Heco: 128, Polygon: 137, Fantom:250, KCC: 321, Arbitrum: 42161, Avalanche: 43114) +"data": "" | null, +} +\`\`\` + + +- [URL_SECURITY_CHECK] + Description: Detection of known phishing websites, malicious sites or other security risks in URLs + Keywords: link detection, phishing website, malicious URL, website security + Respond with a JSON markdown block containing only the extracted values: +\`\`\`json +{ +"type": "URL_SECURITY_CHECK" +"url": "" | null, +} +\`\`\` + +Extract the necessary information(All fields present in the json are important information) and choose the appropriate action(s) based on the text. Return the JSON response following the format above. +important: do not response anything except json` + + + +export const responsePrompt = (apiresult: string, text:string) => `You are a security action detector for blockchain interactions. Your task is to analyze the security API’s response from GoPlus and summary the API result. +API to analyze:“”" +${apiresult} +“”" +user’s request:“” +${text} +“” +Instructions: +1. **Identify the Action**: Analyze the API response to determine which specific action it relates to. +2. **Extract Relevant Information**: From the action and its parameters, extract and highlight the key details. +3. **Formulate a Clear Response**: Combine the action type, extracted information, and an analysis of the results. Provide a clear, concise response based on the security context. Focus on delivering the most relevant answer without unnecessary detail. +- Only reply with your conclusion. +- Do not discuss the safety aspects of the action; just focus on identifying and pointing out any risks. +- Tailor your response to the user’s request, focusing on their specific query.` \ No newline at end of file diff --git a/packages/plugin-goplus/tsconfig.json b/packages/plugin-goplus/tsconfig.json new file mode 100644 index 0000000000..33e9858f48 --- /dev/null +++ b/packages/plugin-goplus/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "declaration": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/packages/plugin-goplus/tsup.config.ts b/packages/plugin-goplus/tsup.config.ts new file mode 100644 index 0000000000..c7bf2d61a7 --- /dev/null +++ b/packages/plugin-goplus/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + dts: true, + sourcemap: true, + splitting: false, + clean: true, +}); From bdebf9c62bedf7a3e24adbb26252c5364be2acf7 Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Fri, 3 Jan 2025 21:04:14 +0100 Subject: [PATCH 12/58] feat: add coinmarketcap-plugin --- agent/package.json | 3 +- agent/src/index.ts | 31 +- packages/plugin-coinmarketcap/package.json | 19 + .../src/actions/getPrice/examples.ts | 46 ++ .../src/actions/getPrice/index.ts | 150 ++++++ .../src/actions/getPrice/service.ts | 71 +++ .../src/actions/getPrice/template.ts | 27 + .../src/actions/getPrice/types.ts | 28 + .../src/actions/getPrice/validation.ts | 16 + .../plugin-coinmarketcap/src/environment.ts | 32 ++ packages/plugin-coinmarketcap/src/index.ts | 12 + packages/plugin-coinmarketcap/src/types.ts | 28 + packages/plugin-coinmarketcap/tsconfig.json | 10 + packages/plugin-coinmarketcap/tsup.config.ts | 19 + pnpm-lock.yaml | 487 +++++++++--------- 15 files changed, 725 insertions(+), 254 deletions(-) create mode 100644 packages/plugin-coinmarketcap/package.json create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/index.ts create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/service.ts create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/template.ts create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/types.ts create mode 100644 packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts create mode 100644 packages/plugin-coinmarketcap/src/environment.ts create mode 100644 packages/plugin-coinmarketcap/src/index.ts create mode 100644 packages/plugin-coinmarketcap/src/types.ts create mode 100644 packages/plugin-coinmarketcap/tsconfig.json create mode 100644 packages/plugin-coinmarketcap/tsup.config.ts diff --git a/agent/package.json b/agent/package.json index f4fa0f33e0..99688494a0 100644 --- a/agent/package.json +++ b/agent/package.json @@ -36,6 +36,7 @@ "@elizaos/plugin-bootstrap": "workspace:*", "@elizaos/plugin-intiface": "workspace:*", "@elizaos/plugin-coinbase": "workspace:*", + "@elizaos/plugin-coinmarketcap": "workspace:*", "@elizaos/plugin-conflux": "workspace:*", "@elizaos/plugin-evm": "workspace:*", "@elizaos/plugin-echochambers": "workspace:*", @@ -72,4 +73,4 @@ "ts-node": "10.9.2", "tsup": "8.3.5" } -} +} \ No newline at end of file diff --git a/agent/src/index.ts b/agent/src/index.ts index 53058cf4ec..e62c783d10 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -1,4 +1,5 @@ import { PostgresDatabaseAdapter } from "@elizaos/adapter-postgres"; +import { RedisClient } from "@elizaos/adapter-redis"; import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite"; import { AutoClientInterface } from "@elizaos/client-auto"; import { DiscordClientInterface } from "@elizaos/client-discord"; @@ -10,31 +11,33 @@ import { TwitterClientInterface } from "@elizaos/client-twitter"; import { AgentRuntime, CacheManager, + CacheStore, Character, + Client, Clients, DbCacheAdapter, defaultCharacter, elizaLogger, FsCacheAdapter, IAgentRuntime, + ICacheManager, IDatabaseAdapter, IDatabaseCacheAdapter, ModelProviderName, settings, stringToUuid, validateCharacterConfig, - CacheStore, - Client, - ICacheManager, - parseBooleanFromText, } from "@elizaos/core"; -import { RedisClient } from "@elizaos/adapter-redis"; import { zgPlugin } from "@elizaos/plugin-0g"; + import { bootstrapPlugin } from "@elizaos/plugin-bootstrap"; import createGoatPlugin from "@elizaos/plugin-goat"; // import { intifacePlugin } from "@elizaos/plugin-intiface"; import { DirectClient } from "@elizaos/client-direct"; +import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; +import { abstractPlugin } from "@elizaos/plugin-abstract"; import { aptosPlugin } from "@elizaos/plugin-aptos"; +import { avalanchePlugin } from "@elizaos/plugin-avalanche"; import { advancedTradePlugin, coinbaseCommercePlugin, @@ -43,33 +46,32 @@ import { tradePlugin, webhookPlugin, } from "@elizaos/plugin-coinbase"; +import { coinmarketcapPlugin } from "@elizaos/plugin-coinmarketcap"; import { confluxPlugin } from "@elizaos/plugin-conflux"; +import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; +import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; import { evmPlugin } from "@elizaos/plugin-evm"; -import { storyPlugin } from "@elizaos/plugin-story"; import { flowPlugin } from "@elizaos/plugin-flow"; import { fuelPlugin } from "@elizaos/plugin-fuel"; + import { imageGenerationPlugin } from "@elizaos/plugin-image-generation"; -import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; import { multiversxPlugin } from "@elizaos/plugin-multiversx"; import { nearPlugin } from "@elizaos/plugin-near"; import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation"; import { createNodePlugin } from "@elizaos/plugin-node"; import { solanaPlugin } from "@elizaos/plugin-solana"; +import { storyPlugin } from "@elizaos/plugin-story"; import { suiPlugin } from "@elizaos/plugin-sui"; import { TEEMode, teePlugin } from "@elizaos/plugin-tee"; import { tonPlugin } from "@elizaos/plugin-ton"; -import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; -import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; -import { abstractPlugin } from "@elizaos/plugin-abstract"; -import { avalanchePlugin } from "@elizaos/plugin-avalanche"; import { webSearchPlugin } from "@elizaos/plugin-web-search"; -import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; +import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; import Database from "better-sqlite3"; import fs from "fs"; +import net from "net"; import path from "path"; import { fileURLToPath } from "url"; import yargs from "yargs"; -import net from "net"; const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file const __dirname = path.dirname(__filename); // get the name of the directory @@ -556,6 +558,9 @@ export async function createAgent( ? nftGenerationPlugin : null, getSecret(character, "ZEROG_PRIVATE_KEY") ? zgPlugin : null, + getSecret(character, "COINMARKETCAP_API_KEY") + ? coinmarketcapPlugin + : null, getSecret(character, "COINBASE_COMMERCE_KEY") ? coinbaseCommercePlugin : null, diff --git a/packages/plugin-coinmarketcap/package.json b/packages/plugin-coinmarketcap/package.json new file mode 100644 index 0000000000..6dbdf24f1f --- /dev/null +++ b/packages/plugin-coinmarketcap/package.json @@ -0,0 +1,19 @@ +{ + "name": "@elizaos/plugin-coinmarketcap", + "version": "0.1.7-alpha.2", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "axios": "^1.6.7", + "zod": "^3.22.4" + }, + "devDependencies": { + "tsup": "^8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch" + } +} \ No newline at end of file diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts new file mode 100644 index 0000000000..5a6713b566 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts @@ -0,0 +1,46 @@ +import { ActionExample } from "@elizaos/core"; + +export const priceExamples: ActionExample[][] = [ + [ + { + user: "{{user1}}", + content: { + text: "What's the current price of Bitcoin?", + }, + }, + { + user: "{{agent}}", + content: { + text: "Let me check the current Bitcoin price for you.", + action: "GET_PRICE", + }, + }, + { + user: "{{agent}}", + content: { + text: "The current price of BTC is 65,432.21 USD", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Check ETH price in EUR", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll check the current Ethereum price in EUR.", + action: "GET_PRICE", + }, + }, + { + user: "{{agent}}", + content: { + text: "The current price of ETH is 2,345.67 EUR", + }, + }, + ], +]; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts new file mode 100644 index 0000000000..915edbff86 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts @@ -0,0 +1,150 @@ +import { + composeContext, + elizaLogger, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { validateCoinMarketCapConfig } from "../../environment"; +import { priceExamples } from "./examples"; +import { createPriceService } from "./service"; +import { getPriceTemplate } from "./template"; +import { GetPriceContent } from "./types"; +import { isGetPriceContent } from "./validation"; + +const extractPriceContent = (response: string): GetPriceContent | null => { + try { + // Find JSON block between ```json and ``` + const jsonMatch = response.match(/```json\n([\s\S]*?)\n```/); + if (!jsonMatch) return null; + + const content = JSON.parse(jsonMatch[1]); + return { + symbol: content.symbol, + currency: content.currency || "USD", + text: content.text || "", + }; + } catch (error) { + elizaLogger.error("Error parsing price content:", error); + return null; + } +}; + +export default { + name: "GET_PRICE", + similes: [ + "CHECK_PRICE", + "PRICE_CHECK", + "GET_CRYPTO_PRICE", + "CHECK_CRYPTO_PRICE", + "GET_TOKEN_PRICE", + "CHECK_TOKEN_PRICE", + ], + validate: async (runtime: IAgentRuntime, message: Memory) => { + await validateCoinMarketCapConfig(runtime); + return true; + }, + description: "Get the current price of a cryptocurrency from CoinMarketCap", + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting CoinMarketCap GET_PRICE handler..."); + + // Initialize or update state + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + try { + // Compose and generate price check content + const priceContext = composeContext({ + state, + template: getPriceTemplate, + }); + + const content = (await generateObjectDeprecated({ + runtime, + context: priceContext, + modelClass: ModelClass.SMALL, + })) as unknown as GetPriceContent; + + elizaLogger.log( + "Generated content:", + JSON.stringify(content, null, 2) + ); + + // Validate content + if (!isGetPriceContent(content)) { + throw new Error("Invalid price check content"); + } + + // Get price from CoinMarketCap + const config = await validateCoinMarketCapConfig(runtime); + + const priceService = createPriceService( + config.COINMARKETCAP_API_KEY + ); + + try { + const priceData = await priceService.getPrice( + content.symbol, + content.currency + ); + elizaLogger.success( + `Price retrieved successfully! ${content.symbol}: ${priceData.price} ${content.currency.toUpperCase()}` + ); + + if (callback) { + callback({ + text: `The current price of ${content.symbol} is ${priceData.price} ${content.currency.toUpperCase()}`, + content: { + symbol: content.symbol, + currency: content.currency, + ...priceData, + }, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error in GET_PRICE handler:", error); + elizaLogger.error("Error details:", { + message: error.message, + stack: error.stack, + content: content, + }); + if (callback) { + callback({ + text: `Error fetching price: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + } catch (error) { + elizaLogger.error("Error in GET_PRICE handler:", error); + elizaLogger.error("Error details:", { + message: error.message, + stack: error.stack, + }); + if (callback) { + callback({ + text: `Error fetching price: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: priceExamples, +} as Action; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts new file mode 100644 index 0000000000..2b2d917092 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts @@ -0,0 +1,71 @@ +import axios from "axios"; +import { ApiResponse, PriceData } from "./types"; + +const BASE_URL = "https://pro-api.coinmarketcap.com/v1"; + +export const createPriceService = (apiKey: string) => { + const client = axios.create({ + baseURL: BASE_URL, + headers: { + "X-CMC_PRO_API_KEY": apiKey, + Accept: "application/json", + }, + }); + + const getPrice = async ( + symbol: string, + currency: string + ): Promise => { + const normalizedSymbol = symbol.toUpperCase().trim(); + const normalizedCurrency = currency.toUpperCase().trim(); + + try { + const response = await client.get( + "/cryptocurrency/quotes/latest", + { + params: { + symbol: normalizedSymbol, + convert: normalizedCurrency, + }, + } + ); + + console.log( + "API Response:", + JSON.stringify(response.data, null, 2) + ); + + const symbolData = response.data.data[normalizedSymbol]; + if (!symbolData) { + throw new Error( + `No data found for symbol: ${normalizedSymbol}` + ); + } + + const quoteData = symbolData.quote[normalizedCurrency]; + if (!quoteData) { + throw new Error( + `No quote data found for currency: ${normalizedCurrency}` + ); + } + + return { + price: quoteData.price, + marketCap: quoteData.market_cap, + volume24h: quoteData.volume_24h, + percentChange24h: quoteData.percent_change_24h, + }; + } catch (error) { + if (axios.isAxiosError(error)) { + const errorMessage = + error.response?.data?.status?.error_message || + error.message; + console.error("API Error:", errorMessage); + throw new Error(`API Error: ${errorMessage}`); + } + throw error; + } + }; + + return { getPrice }; +}; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts new file mode 100644 index 0000000000..46e439a235 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts @@ -0,0 +1,27 @@ +export const getPriceTemplate = `Respond with a JSON object containing BOTH symbol and currency. Currency must default to "USD" if not specified. + +Here are the cryptocurrency symbol mappings: +- bitcoin/btc -> BTC +- ethereum/eth -> ETH +- solana/sol -> SOL +- cardano/ada -> ADA +- ripple/xrp -> XRP +- dogecoin/doge -> DOGE +- polkadot/dot -> DOT +- usdc -> USDC +- tether/usdt -> USDT + +IMPORTANT: Response must ALWAYS include both "symbol" and "currency" fields. + +Example response: +\`\`\`json +{ + "symbol": "BTC", + "currency": "USD" +} +\`\`\` + +{{recentMessages}} + +Extract the cryptocurrency from the most recent message. Always include currency (default "USD"). +Respond with a JSON markdown block containing both symbol and currency.`; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts new file mode 100644 index 0000000000..7b84dde342 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts @@ -0,0 +1,28 @@ +import { Content } from "@elizaos/core"; + +export interface GetPriceContent extends Content { + symbol: string; + currency: string; +} + +export interface PriceData { + price: number; + marketCap: number; + volume24h: number; + percentChange24h: number; +} + +export interface ApiResponse { + data: { + [symbol: string]: { + quote: { + [currency: string]: { + price: number; + market_cap: number; + volume_24h: number; + percent_change_24h: number; + }; + }; + }; + }; +} diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts new file mode 100644 index 0000000000..16bbf67d61 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; +import { GetPriceContent } from "./types"; + +export const GetPriceSchema = z.object({ + symbol: z.string(), + currency: z.string().default("USD"), +}); + +export function isGetPriceContent( + content: GetPriceContent +): content is GetPriceContent { + return ( + typeof content.symbol === "string" && + typeof content.currency === "string" + ); +} diff --git a/packages/plugin-coinmarketcap/src/environment.ts b/packages/plugin-coinmarketcap/src/environment.ts new file mode 100644 index 0000000000..d21d13bfdf --- /dev/null +++ b/packages/plugin-coinmarketcap/src/environment.ts @@ -0,0 +1,32 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const coinmarketcapEnvSchema = z.object({ + COINMARKETCAP_API_KEY: z + .string() + .min(1, "CoinMarketCap API key is required"), +}); + +export type CoinMarketCapConfig = z.infer; + +export async function validateCoinMarketCapConfig( + runtime: IAgentRuntime +): Promise { + try { + const config = { + COINMARKETCAP_API_KEY: runtime.getSetting("COINMARKETCAP_API_KEY"), + }; + + return coinmarketcapEnvSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error( + `CoinMarketCap configuration validation failed:\n${errorMessages}` + ); + } + throw error; + } +} diff --git a/packages/plugin-coinmarketcap/src/index.ts b/packages/plugin-coinmarketcap/src/index.ts new file mode 100644 index 0000000000..5da30cf5ab --- /dev/null +++ b/packages/plugin-coinmarketcap/src/index.ts @@ -0,0 +1,12 @@ +import { Plugin } from "@elizaos/core"; +import getPrice from "./actions/getPrice"; + +export const coinmarketcapPlugin: Plugin = { + name: "coinmarketcap", + description: "CoinMarketCap Plugin for Eliza", + actions: [getPrice], + evaluators: [], + providers: [], +}; + +export default coinmarketcapPlugin; diff --git a/packages/plugin-coinmarketcap/src/types.ts b/packages/plugin-coinmarketcap/src/types.ts new file mode 100644 index 0000000000..7b84dde342 --- /dev/null +++ b/packages/plugin-coinmarketcap/src/types.ts @@ -0,0 +1,28 @@ +import { Content } from "@elizaos/core"; + +export interface GetPriceContent extends Content { + symbol: string; + currency: string; +} + +export interface PriceData { + price: number; + marketCap: number; + volume24h: number; + percentChange24h: number; +} + +export interface ApiResponse { + data: { + [symbol: string]: { + quote: { + [currency: string]: { + price: number; + market_cap: number; + volume_24h: number; + percent_change_24h: number; + }; + }; + }; + }; +} diff --git a/packages/plugin-coinmarketcap/tsconfig.json b/packages/plugin-coinmarketcap/tsconfig.json new file mode 100644 index 0000000000..73993deaaf --- /dev/null +++ b/packages/plugin-coinmarketcap/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/plugin-coinmarketcap/tsup.config.ts b/packages/plugin-coinmarketcap/tsup.config.ts new file mode 100644 index 0000000000..58ed52c499 --- /dev/null +++ b/packages/plugin-coinmarketcap/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], + external: [ + "dotenv", + "fs", + "path", + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90f147c715..42845ec1c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: version: 3.9.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@vitest/eslint-plugin': specifier: 1.0.1 - version: 1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + version: 1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) amqplib: specifier: 0.10.5 version: 0.10.5 @@ -47,7 +47,7 @@ importers: devDependencies: '@commitlint/cli': specifier: 18.6.1 - version: 18.6.1(@types/node@22.10.4)(typescript@5.6.3) + version: 18.6.1(@types/node@22.10.5)(typescript@5.6.3) '@commitlint/config-conventional': specifier: 18.6.3 version: 18.6.3 @@ -95,10 +95,10 @@ importers: version: 2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) vite: specifier: 5.4.11 - version: 5.4.11(@types/node@22.10.4)(terser@5.37.0) + version: 5.4.11(@types/node@22.10.5)(terser@5.37.0) vitest: specifier: 2.1.5 - version: 2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) agent: dependencies: @@ -159,6 +159,9 @@ importers: '@elizaos/plugin-coinbase': specifier: workspace:* version: link:../packages/plugin-coinbase + '@elizaos/plugin-coinmarketcap': + specifier: workspace:* + version: link:../packages/plugin-coinmarketcap '@elizaos/plugin-conflux': specifier: workspace:* version: link:../packages/plugin-conflux @@ -1035,7 +1038,7 @@ importers: version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1093,6 +1096,22 @@ importers: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-coinmarketcap: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + axios: + specifier: ^1.6.7 + version: 1.7.9(debug@4.4.0) + zod: + specifier: ^3.22.4 + version: 3.23.8 + devDependencies: + tsup: + specifier: ^8.3.5 + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-conflux: dependencies: '@elizaos/core': @@ -1118,7 +1137,7 @@ importers: version: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) web3-plugin-zksync: specifier: ^1.0.8 - version: 1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + version: 1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1225,7 +1244,7 @@ importers: version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) packages/plugin-fuel: dependencies: @@ -1237,13 +1256,13 @@ importers: version: 4.0.1 fuels: specifier: 0.97.2 - version: 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + version: 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) tsup: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1313,7 +1332,7 @@ importers: version: 29.5.14 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.10.4) + version: 29.7.0(@types/node@22.10.5) tsup: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) @@ -1376,7 +1395,7 @@ importers: version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.5 - version: 2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1677,7 +1696,7 @@ importers: version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1710,7 +1729,7 @@ importers: version: 1.4.0(starknet@6.18.0(encoding@0.1.13)) vitest: specifier: 2.1.5 - version: 2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1738,7 +1757,7 @@ importers: devDependencies: '@types/node': specifier: ^22.10.1 - version: 22.10.4 + version: 22.10.5 packages/plugin-sui: dependencies: @@ -1768,7 +1787,7 @@ importers: version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1855,7 +1874,7 @@ importers: version: 11.0.3 vitest: specifier: 2.1.5 - version: 2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1947,7 +1966,7 @@ importers: version: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) web3-plugin-zksync: specifier: ^1.0.8 - version: 1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + version: 1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -5861,8 +5880,8 @@ packages: resolution: {integrity: sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==} engines: {node: '>= 18'} - '@octokit/auth-oauth-device@7.1.1': - resolution: {integrity: sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==} + '@octokit/auth-oauth-device@7.1.2': + resolution: {integrity: sha512-gTOIzDeV36OhVfxCl69FmvJix7tJIiU6dlxuzLVAzle7fYfO8UDyddr9B+o4CFQVaMBLMGZ9ak2CWMYcGeZnPw==} engines: {node: '>= 18'} '@octokit/auth-oauth-user@5.1.1': @@ -5893,8 +5912,8 @@ packages: resolution: {integrity: sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==} engines: {node: '>= 18'} - '@octokit/core@6.1.2': - resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} + '@octokit/core@6.1.3': + resolution: {integrity: sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==} engines: {node: '>= 18'} '@octokit/endpoint@10.1.2': @@ -6955,26 +6974,26 @@ packages: resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} engines: {node: '>=6'} - '@shikijs/core@1.25.1': - resolution: {integrity: sha512-0j5k3ZkLTQViOuNzPVyWGoW1zgH3kiFdUT/JOCkTm7TU74mz+dF+NID+YoiCBzHQxgsDpcGYPjKDJRcuVLSt4A==} + '@shikijs/core@1.26.1': + resolution: {integrity: sha512-yeo7sG+WZQblKPclUOKRPwkv1PyoHYkJ4gP9DzhFJbTdueKR7wYTI1vfF/bFi1NTgc545yG/DzvVhZgueVOXMA==} - '@shikijs/engine-javascript@1.25.1': - resolution: {integrity: sha512-zQ7UWKnRCfD/Q1M+XOSyjsbhpE0qv8LUnmn82HYCeOsgAHgUZGEDIQ63bbuK3kU5sQg+2CtI+dPfOqD/mjSY9w==} + '@shikijs/engine-javascript@1.26.1': + resolution: {integrity: sha512-CRhA0b8CaSLxS0E9A4Bzcb3LKBNpykfo9F85ozlNyArxjo2NkijtiwrJZ6eHa+NT5I9Kox2IXVdjUsP4dilsmw==} - '@shikijs/engine-oniguruma@1.25.1': - resolution: {integrity: sha512-iKPMh3H+0USHtWfZ1irfMTH6tGmIUFSnqt3E2K8BgI1VEsqiPh0RYkG2WTwzNiM1/WHN4FzYx/nrKR7PDHiRyw==} + '@shikijs/engine-oniguruma@1.26.1': + resolution: {integrity: sha512-F5XuxN1HljLuvfXv7d+mlTkV7XukC1cawdtOo+7pKgPD83CAB1Sf8uHqP3PK0u7njFH0ZhoXE1r+0JzEgAQ+kg==} - '@shikijs/langs@1.25.1': - resolution: {integrity: sha512-hdYjq9aRJplAzGe2qF51PR9IDgEoyGb4IkXvr3Ts6lEdg4Z8M/kdknKRo2EIuv3IR/aKkJXTlBQRM+wr3t20Ew==} + '@shikijs/langs@1.26.1': + resolution: {integrity: sha512-oz/TQiIqZejEIZbGtn68hbJijAOTtYH4TMMSWkWYozwqdpKR3EXgILneQy26WItmJjp3xVspHdiUxUCws4gtuw==} - '@shikijs/themes@1.25.1': - resolution: {integrity: sha512-JO0lDn4LgGqg5QKvgich5ScUmC2okK+LxM9a3iLUH7YMeI2c8UGXThuJv6sZduS7pdJbYQHPrvWq9t/V4GhpbQ==} + '@shikijs/themes@1.26.1': + resolution: {integrity: sha512-JDxVn+z+wgLCiUhBGx2OQrLCkKZQGzNH3nAxFir4PjUcYiyD8Jdms9izyxIogYmSwmoPTatFTdzyrRKbKlSfPA==} - '@shikijs/types@1.25.1': - resolution: {integrity: sha512-dceqFUoO95eY4tpOj3OGq8wE8EgJ4ey6Me1HQEu5UbwIYszFndEll/bjlB8Kp9wl4fx3uM7n4+y9XCYuDBmcXA==} + '@shikijs/types@1.26.1': + resolution: {integrity: sha512-d4B00TKKAMaHuFYgRf3L0gwtvqpW4hVdVwKcZYbBfAAQXspgkbWqnFfuFl3MDH6gLbsubOcr+prcnsqah3ny7Q==} - '@shikijs/vscode-textmate@9.3.1': - resolution: {integrity: sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==} + '@shikijs/vscode-textmate@10.0.1': + resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} '@sideway/address@4.1.5': resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} @@ -7974,8 +7993,8 @@ packages: '@types/lodash.isstring@4.0.9': resolution: {integrity: sha512-sjGPpa15VBpMns/4s6Blm567JgxLVVu/eCYCe7h/TdQyPCz9lIhaLSISjN7ZC9cDXmUT2IM/4mNRw8OtYirziw==} - '@types/lodash@4.17.13': - resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} + '@types/lodash@4.17.14': + resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} '@types/lru-cache@5.1.1': resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} @@ -8031,8 +8050,8 @@ packages: '@types/node@20.17.9': resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==} - '@types/node@22.10.4': - resolution: {integrity: sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==} + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} @@ -11542,8 +11561,8 @@ packages: resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} engines: {node: '> 0.1.90'} - fast-content-type-parse@2.0.0: - resolution: {integrity: sha512-fCqg/6Sps8tqk8p+kqyKqYfOF0VjPNYrqpLiqNl0RBKmD80B080AJWVV6EkSkscjToNExcXg1+Mfzftrx6+iSA==} + fast-content-type-parse@2.0.1: + resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -12329,8 +12348,8 @@ packages: hast-util-select@4.0.2: resolution: {integrity: sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==} - hast-util-to-estree@3.1.0: - resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==} + hast-util-to-estree@3.1.1: + resolution: {integrity: sha512-IWtwwmPskfSmma9RpzCappDUitC8t5jhAynHhc1m2+5trOgsrp7txscUSavc5Ic8PATyAjfrCK1wgtxh2cICVQ==} hast-util-to-html@9.0.4: resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==} @@ -12659,9 +12678,6 @@ packages: inline-source-map@0.6.3: resolution: {integrity: sha512-1aVsPEsJWMJq/pdMU61CDlm1URcW702MTB4w9/zUjMus6H/Py8o7g68Pr9D4I6QluWGt/KdmswuRhaA05xVR1w==} - inline-style-parser@0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} @@ -14006,8 +14022,8 @@ packages: mdast-util-directive@3.0.0: resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} - mdast-util-find-and-replace@3.0.1: - resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} @@ -17309,8 +17325,8 @@ packages: engines: {node: '>=4'} hasBin: true - shiki@1.25.1: - resolution: {integrity: sha512-/1boRvNYwRW3GLG9Y6dXdnZ/Ha+J5T/5y3hV7TGQUcDSBM185D3FCbXlz2eTGNKG2iWCbWqo+P0yhGKZ4/CUrw==} + shiki@1.26.1: + resolution: {integrity: sha512-Gqg6DSTk3wYqaZ5OaYtzjcdxcBvX5kCy24yvRJEgjT5U+WHlmqCThLuBUx0juyxQBi+6ug53IGeuQS07DWwpcw==} shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -17749,9 +17765,6 @@ packages: engines: {node: '>=4'} hasBin: true - style-to-object@0.4.4: - resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} - style-to-object@1.0.8: resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} @@ -21577,11 +21590,11 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@commitlint/cli@18.6.1(@types/node@22.10.4)(typescript@5.6.3)': + '@commitlint/cli@18.6.1(@types/node@22.10.5)(typescript@5.6.3)': dependencies: '@commitlint/format': 18.6.1 '@commitlint/lint': 18.6.1 - '@commitlint/load': 18.6.1(@types/node@22.10.4)(typescript@5.6.3) + '@commitlint/load': 18.6.1(@types/node@22.10.5)(typescript@5.6.3) '@commitlint/read': 18.6.1 '@commitlint/types': 18.6.1 execa: 5.1.1 @@ -21631,7 +21644,7 @@ snapshots: '@commitlint/rules': 18.6.1 '@commitlint/types': 18.6.1 - '@commitlint/load@18.6.1(@types/node@22.10.4)(typescript@5.6.3)': + '@commitlint/load@18.6.1(@types/node@22.10.5)(typescript@5.6.3)': dependencies: '@commitlint/config-validator': 18.6.1 '@commitlint/execute-rule': 18.6.1 @@ -21639,7 +21652,7 @@ snapshots: '@commitlint/types': 18.6.1 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@5.6.3) - cosmiconfig-typescript-loader: 5.1.0(@types/node@22.10.4)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3) + cosmiconfig-typescript-loader: 5.1.0(@types/node@22.10.5)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -23757,23 +23770,23 @@ snapshots: '@floating-ui/utils@0.2.8': {} - '@fuel-ts/abi-coder@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/abi-coder@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) type-fest: 4.31.0 transitivePeerDependencies: - vitest - '@fuel-ts/abi-typegen@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/abi-typegen@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/versions': 0.97.2 commander: 12.1.0 glob: 10.4.5 @@ -23784,18 +23797,18 @@ snapshots: transitivePeerDependencies: - vitest - '@fuel-ts/account@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/account@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/versions': 0.97.2 '@fuels/vm-asm': 0.58.2 '@noble/curves': 1.8.0 @@ -23808,30 +23821,30 @@ snapshots: - encoding - vitest - '@fuel-ts/address@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/address@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@noble/hashes': 1.7.0 bech32: 2.0.0 transitivePeerDependencies: - vitest - '@fuel-ts/contract@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/contract@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/versions': 0.97.2 '@fuels/vm-asm': 0.58.2 ramda: 0.30.1 @@ -23839,12 +23852,12 @@ snapshots: - encoding - vitest - '@fuel-ts/crypto@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/crypto@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@noble/hashes': 1.7.0 transitivePeerDependencies: - vitest @@ -23853,11 +23866,11 @@ snapshots: dependencies: '@fuel-ts/versions': 0.97.2 - '@fuel-ts/hasher@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/hasher@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@noble/hashes': 1.7.0 transitivePeerDependencies: - vitest @@ -23870,78 +23883,78 @@ snapshots: '@types/bn.js': 5.1.6 bn.js: 5.2.1 - '@fuel-ts/merkle@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/merkle@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/math': 0.97.2 transitivePeerDependencies: - vitest - '@fuel-ts/program@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/program@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuels/vm-asm': 0.58.2 ramda: 0.30.1 transitivePeerDependencies: - encoding - vitest - '@fuel-ts/recipes@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/recipes@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/abi-typegen': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/contract': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-typegen': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/contract': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 - '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) transitivePeerDependencies: - encoding - vitest - '@fuel-ts/script@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/script@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) transitivePeerDependencies: - encoding - vitest - '@fuel-ts/transactions@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/transactions@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) transitivePeerDependencies: - vitest - '@fuel-ts/utils@0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@fuel-ts/utils@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: '@fuel-ts/errors': 0.97.2 '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 '@fuel-ts/versions': 0.97.2 fflate: 0.8.2 - vitest: 2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + vitest: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) '@fuel-ts/versions@0.97.2': dependencies: @@ -25717,9 +25730,9 @@ snapshots: dependencies: '@octokit/auth-app': 7.1.3 '@octokit/auth-unauthenticated': 6.1.0 - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/oauth-app': 7.1.4 - '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.2) + '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.3) '@octokit/types': 13.6.2 '@octokit/webhooks': 13.4.1 @@ -25736,13 +25749,13 @@ snapshots: '@octokit/auth-oauth-app@8.1.1': dependencies: - '@octokit/auth-oauth-device': 7.1.1 + '@octokit/auth-oauth-device': 7.1.2 '@octokit/auth-oauth-user': 5.1.1 '@octokit/request': 9.1.4 '@octokit/types': 13.6.2 universal-user-agent: 7.0.2 - '@octokit/auth-oauth-device@7.1.1': + '@octokit/auth-oauth-device@7.1.2': dependencies: '@octokit/oauth-methods': 5.1.3 '@octokit/request': 9.1.4 @@ -25751,7 +25764,7 @@ snapshots: '@octokit/auth-oauth-user@5.1.1': dependencies: - '@octokit/auth-oauth-device': 7.1.1 + '@octokit/auth-oauth-device': 7.1.2 '@octokit/oauth-methods': 5.1.3 '@octokit/request': 9.1.4 '@octokit/types': 13.6.2 @@ -25790,7 +25803,7 @@ snapshots: before-after-hook: 2.2.3 universal-user-agent: 6.0.1 - '@octokit/core@6.1.2': + '@octokit/core@6.1.3': dependencies: '@octokit/auth-token': 5.1.1 '@octokit/graphql': 8.1.2 @@ -25841,7 +25854,7 @@ snapshots: '@octokit/auth-oauth-app': 8.1.1 '@octokit/auth-oauth-user': 5.1.1 '@octokit/auth-unauthenticated': 6.1.0 - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/oauth-authorization-url': 7.1.1 '@octokit/oauth-methods': 5.1.3 '@types/aws-lambda': 8.10.147 @@ -25866,18 +25879,18 @@ snapshots: '@octokit/plugin-enterprise-rest@6.0.1': {} - '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.2)': + '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.3)': dependencies: - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@5.2.0)': dependencies: '@octokit/core': 5.2.0 '@octokit/types': 13.6.2 - '@octokit/plugin-paginate-rest@11.3.6(@octokit/core@6.1.2)': + '@octokit/plugin-paginate-rest@11.3.6(@octokit/core@6.1.3)': dependencies: - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/types': 13.6.2 '@octokit/plugin-paginate-rest@6.1.2(@octokit/core@4.2.4(encoding@0.1.13))': @@ -25899,9 +25912,9 @@ snapshots: '@octokit/core': 5.2.0 '@octokit/types': 13.6.2 - '@octokit/plugin-rest-endpoint-methods@13.2.6(@octokit/core@6.1.2)': + '@octokit/plugin-rest-endpoint-methods@13.2.6(@octokit/core@6.1.3)': dependencies: - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/types': 13.6.2 '@octokit/plugin-rest-endpoint-methods@7.2.3(@octokit/core@4.2.4(encoding@0.1.13))': @@ -25909,16 +25922,16 @@ snapshots: '@octokit/core': 4.2.4(encoding@0.1.13) '@octokit/types': 10.0.0 - '@octokit/plugin-retry@7.1.2(@octokit/core@6.1.2)': + '@octokit/plugin-retry@7.1.2(@octokit/core@6.1.3)': dependencies: - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/request-error': 6.1.6 '@octokit/types': 13.6.2 bottleneck: 2.19.5 - '@octokit/plugin-throttling@9.3.2(@octokit/core@6.1.2)': + '@octokit/plugin-throttling@9.3.2(@octokit/core@6.1.3)': dependencies: - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/types': 13.6.2 bottleneck: 2.19.5 @@ -25961,7 +25974,7 @@ snapshots: '@octokit/endpoint': 10.1.2 '@octokit/request-error': 6.1.6 '@octokit/types': 13.6.2 - fast-content-type-parse: 2.0.0 + fast-content-type-parse: 2.0.1 universal-user-agent: 7.0.2 '@octokit/rest@19.0.11(encoding@0.1.13)': @@ -26736,7 +26749,7 @@ snapshots: '@react-icons/all-files': 4.1.0(react@18.3.1) '@types/big.js': 6.2.2 '@types/bn.js': 5.1.6 - '@types/lodash': 4.17.13 + '@types/lodash': 4.17.14 big.js: 6.2.2 bn.js: 5.2.1 lodash: 4.17.21 @@ -27133,40 +27146,40 @@ snapshots: '@sentry/types': 5.30.0 tslib: 1.14.1 - '@shikijs/core@1.25.1': + '@shikijs/core@1.26.1': dependencies: - '@shikijs/engine-javascript': 1.25.1 - '@shikijs/engine-oniguruma': 1.25.1 - '@shikijs/types': 1.25.1 - '@shikijs/vscode-textmate': 9.3.1 + '@shikijs/engine-javascript': 1.26.1 + '@shikijs/engine-oniguruma': 1.26.1 + '@shikijs/types': 1.26.1 + '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 hast-util-to-html: 9.0.4 - '@shikijs/engine-javascript@1.25.1': + '@shikijs/engine-javascript@1.26.1': dependencies: - '@shikijs/types': 1.25.1 - '@shikijs/vscode-textmate': 9.3.1 + '@shikijs/types': 1.26.1 + '@shikijs/vscode-textmate': 10.0.1 oniguruma-to-es: 0.10.0 - '@shikijs/engine-oniguruma@1.25.1': + '@shikijs/engine-oniguruma@1.26.1': dependencies: - '@shikijs/types': 1.25.1 - '@shikijs/vscode-textmate': 9.3.1 + '@shikijs/types': 1.26.1 + '@shikijs/vscode-textmate': 10.0.1 - '@shikijs/langs@1.25.1': + '@shikijs/langs@1.26.1': dependencies: - '@shikijs/types': 1.25.1 + '@shikijs/types': 1.26.1 - '@shikijs/themes@1.25.1': + '@shikijs/themes@1.26.1': dependencies: - '@shikijs/types': 1.25.1 + '@shikijs/types': 1.26.1 - '@shikijs/types@1.25.1': + '@shikijs/types@1.26.1': dependencies: - '@shikijs/vscode-textmate': 9.3.1 + '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 - '@shikijs/vscode-textmate@9.3.1': {} + '@shikijs/vscode-textmate@10.0.1': {} '@sideway/address@4.1.5': dependencies: @@ -28564,9 +28577,9 @@ snapshots: '@types/lodash.isstring@4.0.9': dependencies: - '@types/lodash': 4.17.13 + '@types/lodash': 4.17.14 - '@types/lodash@4.17.13': {} + '@types/lodash@4.17.14': {} '@types/lru-cache@5.1.1': {} @@ -28617,7 +28630,7 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/node@22.10.4': + '@types/node@22.10.5': dependencies: undici-types: 6.20.0 @@ -28997,13 +29010,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@vitest/eslint-plugin@1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: eslint: 9.16.0(jiti@2.4.2) optionalDependencies: '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3) typescript: 5.6.3 - vitest: 2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + vitest: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) '@vitest/expect@2.1.4': dependencies: @@ -29019,21 +29032,21 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.4(vite@5.4.11(@types/node@22.10.4)(terser@5.37.0))': + '@vitest/mocker@2.1.4(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0))': dependencies: '@vitest/spy': 2.1.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) - '@vitest/mocker@2.1.5(vite@5.4.11(@types/node@22.10.4)(terser@5.37.0))': + '@vitest/mocker@2.1.5(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0))': dependencies: '@vitest/spy': 2.1.5 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) '@vitest/pretty-format@2.1.4': dependencies: @@ -31516,9 +31529,9 @@ snapshots: dependencies: layout-base: 2.0.1 - cosmiconfig-typescript-loader@5.1.0(@types/node@22.10.4)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3): + cosmiconfig-typescript-loader@5.1.0(@types/node@22.10.5)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3): dependencies: - '@types/node': 22.10.4 + '@types/node': 22.10.5 cosmiconfig: 8.3.6(typescript@5.6.3) jiti: 1.21.7 typescript: 5.6.3 @@ -31601,13 +31614,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.10.4): + create-jest@29.7.0(@types/node@22.10.5): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.4) + jest-config: 29.7.0(@types/node@22.10.5) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -33446,7 +33459,7 @@ snapshots: eyes@0.1.8: {} - fast-content-type-parse@2.0.0: {} + fast-content-type-parse@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -33801,24 +33814,24 @@ snapshots: fsevents@2.3.3: optional: true - fuels@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)): + fuels@0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)): dependencies: - '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/abi-typegen': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/contract': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-coder': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/abi-typegen': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/account': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/address': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/contract': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/crypto': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/errors': 0.97.2 - '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/hasher': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/interfaces': 0.97.2 '@fuel-ts/math': 0.97.2 - '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/recipes': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/script': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) - '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/merkle': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/program': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/recipes': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/script': 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/transactions': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + '@fuel-ts/utils': 0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) '@fuel-ts/versions': 0.97.2 bundle-require: 5.1.0(esbuild@0.24.2) chalk: 4.1.2 @@ -34310,7 +34323,7 @@ snapshots: hard-rejection@2.1.0: {} - hardhat@2.22.17(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10): + hardhat@2.22.17(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 '@metamask/eth-sig-util': 4.0.1 @@ -34357,7 +34370,7 @@ snapshots: uuid: 8.3.2 ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3) + ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3) typescript: 5.6.3 transitivePeerDependencies: - bufferutil @@ -34476,7 +34489,7 @@ snapshots: unist-util-visit: 2.0.3 zwitch: 1.0.5 - hast-util-to-estree@3.1.0: + hast-util-to-estree@3.1.1: dependencies: '@types/estree': 1.0.6 '@types/estree-jsx': 1.0.5 @@ -34491,7 +34504,7 @@ snapshots: mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 - style-to-object: 0.4.4 + style-to-object: 1.0.8 unist-util-position: 5.0.0 zwitch: 2.0.4 transitivePeerDependencies: @@ -34901,8 +34914,6 @@ snapshots: dependencies: source-map: 0.5.7 - inline-style-parser@0.1.1: {} - inline-style-parser@0.2.4: {} inquirer@8.2.6: @@ -35485,16 +35496,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.10.4): + jest-cli@29.7.0(@types/node@22.10.5): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.4) + create-jest: 29.7.0(@types/node@22.10.5) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.10.4) + jest-config: 29.7.0(@types/node@22.10.5) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -35647,7 +35658,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@22.10.4): + jest-config@29.7.0(@types/node@22.10.5): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -35672,7 +35683,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.10.4 + '@types/node': 22.10.5 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -35953,12 +35964,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.10.4): + jest@29.7.0(@types/node@22.10.5): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.10.4) + jest-cli: 29.7.0(@types/node@22.10.5) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -36802,7 +36813,7 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-find-and-replace@3.0.1: + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 @@ -36842,7 +36853,7 @@ snapshots: '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.1 + mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 mdast-util-gfm-footnote@2.0.0: @@ -38193,13 +38204,13 @@ snapshots: octokit@4.0.3: dependencies: '@octokit/app': 15.1.1 - '@octokit/core': 6.1.2 + '@octokit/core': 6.1.3 '@octokit/oauth-app': 7.1.4 - '@octokit/plugin-paginate-graphql': 5.2.4(@octokit/core@6.1.2) - '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.2) - '@octokit/plugin-rest-endpoint-methods': 13.2.6(@octokit/core@6.1.2) - '@octokit/plugin-retry': 7.1.2(@octokit/core@6.1.2) - '@octokit/plugin-throttling': 9.3.2(@octokit/core@6.1.2) + '@octokit/plugin-paginate-graphql': 5.2.4(@octokit/core@6.1.3) + '@octokit/plugin-paginate-rest': 11.3.6(@octokit/core@6.1.3) + '@octokit/plugin-rest-endpoint-methods': 13.2.6(@octokit/core@6.1.3) + '@octokit/plugin-retry': 7.1.2(@octokit/core@6.1.3) + '@octokit/plugin-throttling': 9.3.2(@octokit/core@6.1.3) '@octokit/request-error': 6.1.6 '@octokit/types': 13.6.2 @@ -40413,7 +40424,7 @@ snapshots: dependencies: '@types/estree': 1.0.6 '@types/hast': 3.0.4 - hast-util-to-estree: 3.1.0 + hast-util-to-estree: 3.1.1 transitivePeerDependencies: - supports-color @@ -40432,7 +40443,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 emoticon: 4.1.0 - mdast-util-find-and-replace: 3.0.1 + mdast-util-find-and-replace: 3.0.2 node-emoji: 2.2.0 unified: 11.0.5 @@ -41021,15 +41032,15 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - shiki@1.25.1: + shiki@1.26.1: dependencies: - '@shikijs/core': 1.25.1 - '@shikijs/engine-javascript': 1.25.1 - '@shikijs/engine-oniguruma': 1.25.1 - '@shikijs/langs': 1.25.1 - '@shikijs/themes': 1.25.1 - '@shikijs/types': 1.25.1 - '@shikijs/vscode-textmate': 9.3.1 + '@shikijs/core': 1.26.1 + '@shikijs/engine-javascript': 1.26.1 + '@shikijs/engine-oniguruma': 1.26.1 + '@shikijs/langs': 1.26.1 + '@shikijs/themes': 1.26.1 + '@shikijs/types': 1.26.1 + '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 shimmer@1.2.1: {} @@ -41541,10 +41552,6 @@ snapshots: minimist: 1.2.8 through: 2.3.8 - style-to-object@0.4.4: - dependencies: - inline-style-parser: 0.1.1 - style-to-object@1.0.8: dependencies: inline-style-parser: 0.2.4 @@ -42081,14 +42088,14 @@ snapshots: optionalDependencies: '@swc/core': 1.10.4(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3): + ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.10.4 + '@types/node': 22.10.5 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -42329,7 +42336,7 @@ snapshots: lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - shiki: 1.25.1 + shiki: 1.26.1 typescript: 5.6.3 yaml: 2.7.0 @@ -42843,12 +42850,12 @@ snapshots: - utf-8-validate - zod - vite-node@2.1.4(@types/node@22.10.4)(terser@5.37.0): + vite-node@2.1.4(@types/node@22.10.5)(terser@5.37.0): dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@8.1.1) pathe: 1.1.2 - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) transitivePeerDependencies: - '@types/node' - less @@ -42860,13 +42867,13 @@ snapshots: - supports-color - terser - vite-node@2.1.5(@types/node@22.10.4)(terser@5.37.0): + vite-node@2.1.5(@types/node@22.10.5)(terser@5.37.0): dependencies: cac: 6.7.14 debug: 4.4.0(supports-color@8.1.1) es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) transitivePeerDependencies: - '@types/node' - less @@ -42910,13 +42917,13 @@ snapshots: dependencies: vite: link:client/@tanstack/router-plugin/vite - vite@5.4.11(@types/node@22.10.4)(terser@5.37.0): + vite@5.4.11(@types/node@22.10.5)(terser@5.37.0): dependencies: esbuild: 0.21.5 postcss: 8.4.49 rollup: 4.29.1 optionalDependencies: - '@types/node': 22.10.4 + '@types/node': 22.10.5 fsevents: 2.3.3 terser: 5.37.0 @@ -42930,10 +42937,10 @@ snapshots: fsevents: 2.3.3 terser: 5.37.0 - vitest@2.1.4(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): + vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): dependencies: '@vitest/expect': 2.1.4 - '@vitest/mocker': 2.1.4(vite@5.4.11(@types/node@22.10.4)(terser@5.37.0)) + '@vitest/mocker': 2.1.4(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0)) '@vitest/pretty-format': 2.1.8 '@vitest/runner': 2.1.4 '@vitest/snapshot': 2.1.4 @@ -42949,11 +42956,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) - vite-node: 2.1.4(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) + vite-node: 2.1.4(@types/node@22.10.5)(terser@5.37.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.10.4 + '@types/node': 22.10.5 jsdom: 25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10) transitivePeerDependencies: - less @@ -42966,10 +42973,10 @@ snapshots: - supports-color - terser - vitest@2.1.5(@types/node@22.10.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): + vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): dependencies: '@vitest/expect': 2.1.5 - '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.10.4)(terser@5.37.0)) + '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0)) '@vitest/pretty-format': 2.1.8 '@vitest/runner': 2.1.5 '@vitest/snapshot': 2.1.5 @@ -42985,11 +42992,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.11(@types/node@22.10.4)(terser@5.37.0) - vite-node: 2.1.5(@types/node@22.10.4)(terser@5.37.0) + vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) + vite-node: 2.1.5(@types/node@22.10.5)(terser@5.37.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.10.4 + '@types/node': 22.10.5 jsdom: 25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10) transitivePeerDependencies: - less @@ -43005,7 +43012,7 @@ snapshots: vitest@2.1.5(@types/node@22.8.4)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): dependencies: '@vitest/expect': 2.1.5 - '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.10.4)(terser@5.37.0)) + '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0)) '@vitest/pretty-format': 2.1.8 '@vitest/runner': 2.1.5 '@vitest/snapshot': 2.1.5 @@ -43253,10 +43260,10 @@ snapshots: - encoding - utf-8-validate - web3-plugin-zksync@1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)): + web3-plugin-zksync@1.0.8(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)(web3@4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)): dependencies: ethereum-cryptography: 2.2.1 - hardhat: 2.22.17(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.4)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.17(bufferutil@4.0.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) web3: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: - bufferutil From be3117066b72c5149cc6ec21d90eb29042116ab2 Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Fri, 3 Jan 2025 21:05:17 +0100 Subject: [PATCH 13/58] fix: remove duplicate log --- .../src/actions/getPrice/index.ts | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts index 915edbff86..61ca090c02 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts @@ -16,24 +16,6 @@ import { getPriceTemplate } from "./template"; import { GetPriceContent } from "./types"; import { isGetPriceContent } from "./validation"; -const extractPriceContent = (response: string): GetPriceContent | null => { - try { - // Find JSON block between ```json and ``` - const jsonMatch = response.match(/```json\n([\s\S]*?)\n```/); - if (!jsonMatch) return null; - - const content = JSON.parse(jsonMatch[1]); - return { - symbol: content.symbol, - currency: content.currency || "USD", - text: content.text || "", - }; - } catch (error) { - elizaLogger.error("Error parsing price content:", error); - return null; - } -}; - export default { name: "GET_PRICE", similes: [ @@ -78,11 +60,6 @@ export default { modelClass: ModelClass.SMALL, })) as unknown as GetPriceContent; - elizaLogger.log( - "Generated content:", - JSON.stringify(content, null, 2) - ); - // Validate content if (!isGetPriceContent(content)) { throw new Error("Invalid price check content"); @@ -118,11 +95,6 @@ export default { return true; } catch (error) { elizaLogger.error("Error in GET_PRICE handler:", error); - elizaLogger.error("Error details:", { - message: error.message, - stack: error.stack, - content: content, - }); if (callback) { callback({ text: `Error fetching price: ${error.message}`, @@ -133,10 +105,6 @@ export default { } } catch (error) { elizaLogger.error("Error in GET_PRICE handler:", error); - elizaLogger.error("Error details:", { - message: error.message, - stack: error.stack, - }); if (callback) { callback({ text: `Error fetching price: ${error.message}`, From a44c8a7c210f96673e53edbb421f3b73d6c3226a Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Fri, 3 Jan 2025 21:14:20 +0100 Subject: [PATCH 14/58] docs: add readme.md --- packages/plugin-coinmarketcap/README.md | 127 ++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 packages/plugin-coinmarketcap/README.md diff --git a/packages/plugin-coinmarketcap/README.md b/packages/plugin-coinmarketcap/README.md new file mode 100644 index 0000000000..08e6294882 --- /dev/null +++ b/packages/plugin-coinmarketcap/README.md @@ -0,0 +1,127 @@ +# @elizaos/plugin-coinmarketcap + +A plugin for Eliza that enables cryptocurrency price checking using the CoinMarketCap API. + +## Features + +- Real-time cryptocurrency price checking +- Support for multiple cryptocurrencies (BTC, ETH, SOL, etc.) +- Currency conversion (USD, EUR, etc.) +- Detailed price and market data +- Natural language processing for price queries + +## Installation + +```bash +npm install @elizaos/plugin-coinmarketcap +``` + +## Configuration + +1. Get your API key from [CoinMarketCap](https://pro.coinmarketcap.com) + +2. Set up your environment variables: + +```bash +COINMARKETCAP_API_KEY=your_api_key +``` + +3. Register the plugin in your Eliza configuration: + +```typescript +import { CoinMarketCapPlugin } from "@elizaos/plugin-coinmarketcap"; + +// In your Eliza configuration +plugins: [ + new CoinMarketCapPlugin(), + // ... other plugins +]; +``` + +## Usage + +The plugin responds to natural language queries about cryptocurrency prices. Here are some examples: + +```plaintext +"What's the current price of Bitcoin?" +"Show me ETH price in USD" +"Get the price of SOL" +``` + +### Supported Cryptocurrencies + +The plugin supports major cryptocurrencies including: + +- Bitcoin (BTC) +- Ethereum (ETH) +- Solana (SOL) +- USD Coin (USDC) +- And many more... + +### Available Actions + +#### GET_PRICE + +Fetches the current price of a cryptocurrency. + +```typescript +// Example response format +{ + symbol: "BTC", + price: 50000.00, + currency: "USD", + marketCap: 1000000000000, + volume24h: 50000000000, + percentChange24h: 2.5 +} +``` + +## API Reference + +### Environment Variables + +| Variable | Description | Required | +| --------------------- | -------------------------- | -------- | +| COINMARKETCAP_API_KEY | Your CoinMarketCap API key | Yes | + +### Types + +```typescript +interface PriceData { + price: number; + marketCap: number; + volume24h: number; + percentChange24h: number; +} + +interface GetPriceContent { + symbol: string; + currency: string; +} +``` + +## Error Handling + +The plugin includes comprehensive error handling for: + +- Invalid API keys +- Rate limiting +- Network timeouts +- Invalid cryptocurrency symbols +- Unsupported currencies + +## Rate Limits + +CoinMarketCap API has different rate limits based on your subscription plan. Please refer to [CoinMarketCap's pricing page](https://coinmarketcap.com/api/pricing/) for detailed information. + +## Support + +For support, please open an issue in the repository or reach out to the maintainers: + +- Discord: 0xspit + +## Links + +- [CoinMarketCap API Documentation](https://coinmarketcap.com/api/documentation/v1/) + +- [GitHub Repository](https://github.com/elizaOS/eliza/tree/main/packages/plugin-coinmarketcap) From 878727e5f81fbe750df902b892c8961c30f8ecc3 Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 03:35:01 -0500 Subject: [PATCH 15/58] add coincap api --- .env.example | 3 + agent/src/index.ts | 1 + .../src/actions/getPrice/index.ts | 25 +++-- .../src/actions/getPrice/service.ts | 101 +++++++++++------- .../src/actions/getPrice/template.ts | 25 ++--- .../src/actions/getPrice/types.ts | 1 + .../src/actions/getPrice/validation.ts | 4 +- .../plugin-coinmarketcap/src/environment.ts | 4 +- packages/plugin-coinmarketcap/src/index.ts | 2 +- 9 files changed, 100 insertions(+), 66 deletions(-) diff --git a/.env.example b/.env.example index 2c0bdbe3bd..a01774e20d 100644 --- a/.env.example +++ b/.env.example @@ -256,6 +256,9 @@ CONFLUX_ESPACE_PRIVATE_KEY= CONFLUX_ESPACE_RPC_URL= CONFLUX_MEME_CONTRACT_ADDRESS= +# Coinmarketcap Configuration +COINMARKETCAP_API_KEY= + # ZeroG ZEROG_INDEXER_RPC= ZEROG_EVM_RPC= diff --git a/agent/src/index.ts b/agent/src/index.ts index e62c783d10..55e1e2257c 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -531,6 +531,7 @@ export async function createAgent( ? confluxPlugin : null, nodePlugin, + coinmarketcapPlugin, getSecret(character, "TAVILY_API_KEY") ? webSearchPlugin : null, getSecret(character, "SOLANA_PUBLIC_KEY") || (getSecret(character, "WALLET_PUBLIC_KEY") && diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts index 61ca090c02..8fe2789781 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts @@ -26,11 +26,11 @@ export default { "GET_TOKEN_PRICE", "CHECK_TOKEN_PRICE", ], - validate: async (runtime: IAgentRuntime, message: Memory) => { - await validateCoinMarketCapConfig(runtime); + validate: async (_runtime: IAgentRuntime, _message: Memory) => { + // Always validate to true since we have a fallback API return true; }, - description: "Get the current price of a cryptocurrency from CoinMarketCap", + description: "Get the current price of a cryptocurrency from CoinMarketCap or CoinCap", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -38,7 +38,7 @@ export default { _options: { [key: string]: unknown }, callback?: HandlerCallback ): Promise => { - elizaLogger.log("Starting CoinMarketCap GET_PRICE handler..."); + elizaLogger.log("Starting crypto price check handler..."); // Initialize or update state if (!state) { @@ -65,27 +65,26 @@ export default { throw new Error("Invalid price check content"); } - // Get price from CoinMarketCap - const config = await validateCoinMarketCapConfig(runtime); - - const priceService = createPriceService( - config.COINMARKETCAP_API_KEY - ); + // Get API key if available + const apiKey = runtime.getSetting("COINMARKETCAP_API_KEY"); + const priceService = createPriceService(apiKey); try { const priceData = await priceService.getPrice( content.symbol, - content.currency + content.currency, + content.cryptoName ); elizaLogger.success( - `Price retrieved successfully! ${content.symbol}: ${priceData.price} ${content.currency.toUpperCase()}` + `Price retrieved successfully! ${content.cryptoName}: ${priceData.price} ${content.currency.toUpperCase()}` ); if (callback) { callback({ - text: `The current price of ${content.symbol} is ${priceData.price} ${content.currency.toUpperCase()}`, + text: `The current price of ${content.cryptoName} is ${priceData.price} ${content.currency.toUpperCase()}`, content: { symbol: content.symbol, + cryptoName: content.cryptoName, currency: content.currency, ...priceData, }, diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts index 2b2d917092..5802103114 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts @@ -1,64 +1,93 @@ import axios from "axios"; import { ApiResponse, PriceData } from "./types"; -const BASE_URL = "https://pro-api.coinmarketcap.com/v1"; +const COINMARKETCAP_BASE_URL = "https://pro-api.coinmarketcap.com/v1"; +const COINCAP_BASE_URL = "https://api.coincap.io/v2"; -export const createPriceService = (apiKey: string) => { - const client = axios.create({ - baseURL: BASE_URL, +export const createPriceService = (apiKey?: string) => { + const coinmarketcapClient = apiKey ? axios.create({ + baseURL: COINMARKETCAP_BASE_URL, headers: { "X-CMC_PRO_API_KEY": apiKey, Accept: "application/json", }, + }) : null; + + const coincapClient = axios.create({ + baseURL: COINCAP_BASE_URL, + headers: { + Accept: "application/json", + }, }); const getPrice = async ( symbol: string, - currency: string + currency: string, + cryptoName: string, ): Promise => { + const normalizedCrypto = cryptoName.toLowerCase().trim(); const normalizedSymbol = symbol.toUpperCase().trim(); const normalizedCurrency = currency.toUpperCase().trim(); try { - const response = await client.get( - "/cryptocurrency/quotes/latest", - { - params: { - symbol: normalizedSymbol, - convert: normalizedCurrency, - }, + if (coinmarketcapClient) { + // Try CoinMarketCap first if API key is available + const response = await coinmarketcapClient.get( + "/cryptocurrency/quotes/latest", + { + params: { + symbol: normalizedSymbol, + convert: normalizedCurrency, + }, + } + ); + + const symbolData = response.data.data[normalizedSymbol]; + if (!symbolData) { + throw new Error( + `No data found for symbol: ${normalizedSymbol}` + ); } - ); - console.log( - "API Response:", - JSON.stringify(response.data, null, 2) - ); + const quoteData = symbolData.quote[normalizedCurrency]; + if (!quoteData) { + throw new Error( + `No quote data found for currency: ${normalizedCurrency}` + ); + } - const symbolData = response.data.data[normalizedSymbol]; - if (!symbolData) { - throw new Error( - `No data found for symbol: ${normalizedSymbol}` - ); - } + return { + price: quoteData.price, + marketCap: quoteData.market_cap, + volume24h: quoteData.volume_24h, + percentChange24h: quoteData.percent_change_24h, + }; + } else { + // Fallback to CoinCap API + // CoinCap only supports USD, so we'll need to handle currency conversion differently + if (normalizedCurrency !== "USD") { + throw new Error("CoinCap API only supports USD currency"); + } - const quoteData = symbolData.quote[normalizedCurrency]; - if (!quoteData) { - throw new Error( - `No quote data found for currency: ${normalizedCurrency}` - ); - } + const response = await coincapClient.get(`/assets/${normalizedCrypto}`); + const data = response.data.data; - return { - price: quoteData.price, - marketCap: quoteData.market_cap, - volume24h: quoteData.volume_24h, - percentChange24h: quoteData.percent_change_24h, - }; + if (!data) { + throw new Error(`No data found for cryptocurrency: ${normalizedCrypto}`); + } + + return { + price: parseFloat(data.priceUsd), + marketCap: parseFloat(data.marketCapUsd), + volume24h: parseFloat(data.volumeUsd24Hr), + percentChange24h: parseFloat(data.changePercent24Hr), + }; + } } catch (error) { if (axios.isAxiosError(error)) { const errorMessage = error.response?.data?.status?.error_message || + error.response?.data?.error || error.message; console.error("API Error:", errorMessage); throw new Error(`API Error: ${errorMessage}`); @@ -68,4 +97,4 @@ export const createPriceService = (apiKey: string) => { }; return { getPrice }; -}; +} diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts index 46e439a235..ec3be26389 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts @@ -1,22 +1,23 @@ -export const getPriceTemplate = `Respond with a JSON object containing BOTH symbol and currency. Currency must default to "USD" if not specified. +export const getPriceTemplate = `Respond with a JSON object containing symbol, cryptoName, and currency. Currency must default to "USD" if not specified. Here are the cryptocurrency symbol mappings: -- bitcoin/btc -> BTC -- ethereum/eth -> ETH -- solana/sol -> SOL -- cardano/ada -> ADA -- ripple/xrp -> XRP -- dogecoin/doge -> DOGE -- polkadot/dot -> DOT -- usdc -> USDC -- tether/usdt -> USDT +- bitcoin/btc -> BTC (cryptoName: bitcoin) +- ethereum/eth -> ETH (cryptoName: ethereum) +- solana/sol -> SOL (cryptoName: solana) +- cardano/ada -> ADA (cryptoName: cardano) +- ripple/xrp -> XRP (cryptoName: ripple) +- dogecoin/doge -> DOGE (cryptoName: dogecoin) +- polkadot/dot -> DOT (cryptoName: polkadot) +- usdc -> USDC (cryptoName: usd-coin) +- tether/usdt -> USDT (cryptoName: tether) -IMPORTANT: Response must ALWAYS include both "symbol" and "currency" fields. +IMPORTANT: Response must ALWAYS include "symbol", "cryptoName", and "currency" fields. Example response: \`\`\`json { "symbol": "BTC", + "cryptoName": "bitcoin", "currency": "USD" } \`\`\` @@ -24,4 +25,4 @@ Example response: {{recentMessages}} Extract the cryptocurrency from the most recent message. Always include currency (default "USD"). -Respond with a JSON markdown block containing both symbol and currency.`; +Respond with a JSON markdown block containing symbol, cryptoName, and currency.`; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts index 7b84dde342..6c5b15709a 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts @@ -3,6 +3,7 @@ import { Content } from "@elizaos/core"; export interface GetPriceContent extends Content { symbol: string; currency: string; + cryptoName: string; } export interface PriceData { diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts index 16bbf67d61..caa6165213 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts @@ -4,6 +4,7 @@ import { GetPriceContent } from "./types"; export const GetPriceSchema = z.object({ symbol: z.string(), currency: z.string().default("USD"), + cryptoName: z.string(), }); export function isGetPriceContent( @@ -11,6 +12,7 @@ export function isGetPriceContent( ): content is GetPriceContent { return ( typeof content.symbol === "string" && - typeof content.currency === "string" + typeof content.currency === "string" && + typeof content.cryptoName === "string" ); } diff --git a/packages/plugin-coinmarketcap/src/environment.ts b/packages/plugin-coinmarketcap/src/environment.ts index d21d13bfdf..33ca49f5f2 100644 --- a/packages/plugin-coinmarketcap/src/environment.ts +++ b/packages/plugin-coinmarketcap/src/environment.ts @@ -2,9 +2,7 @@ import { IAgentRuntime } from "@elizaos/core"; import { z } from "zod"; export const coinmarketcapEnvSchema = z.object({ - COINMARKETCAP_API_KEY: z - .string() - .min(1, "CoinMarketCap API key is required"), + COINMARKETCAP_API_KEY: z.string().optional(), }); export type CoinMarketCapConfig = z.infer; diff --git a/packages/plugin-coinmarketcap/src/index.ts b/packages/plugin-coinmarketcap/src/index.ts index 5da30cf5ab..6b79825e0e 100644 --- a/packages/plugin-coinmarketcap/src/index.ts +++ b/packages/plugin-coinmarketcap/src/index.ts @@ -3,7 +3,7 @@ import getPrice from "./actions/getPrice"; export const coinmarketcapPlugin: Plugin = { name: "coinmarketcap", - description: "CoinMarketCap Plugin for Eliza", + description: "Plugin for cryptocurrency price checking using CoinMarketCap API with fallback to CoinCap API when no API key is provided", actions: [getPrice], evaluators: [], providers: [], From c3d2866d5a887cb04bb20c0a8944a4b36e0afdb2 Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 04:08:22 -0500 Subject: [PATCH 16/58] fix output formatting --- packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts | 2 +- packages/plugin-coinmarketcap/src/actions/getPrice/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts index 5a6713b566..c2680e6f54 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts @@ -18,7 +18,7 @@ export const priceExamples: ActionExample[][] = [ { user: "{{agent}}", content: { - text: "The current price of BTC is 65,432.21 USD", + text: "The current price of BTC is 65,432.21 USD. \nmarket cap is 1,234,567,890 USD \nvolume 24h is 1,234,567,890 USD \npercent change 24h is 1,234,567,890 USD", }, }, ], diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts index 8fe2789781..955f8656b6 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts @@ -81,7 +81,7 @@ export default { if (callback) { callback({ - text: `The current price of ${content.cryptoName} is ${priceData.price} ${content.currency.toUpperCase()}`, + text: `The current price of ${content.cryptoName} ${content.symbol} is ${(priceData.price).toLocaleString()} ${content.currency.toUpperCase()} \nMarket Cap is ${(priceData.marketCap).toLocaleString()} ${content.currency.toUpperCase()} \n24h Volume is ${(priceData.volume24h).toLocaleString()} ${content.currency.toUpperCase()} \nThe 24h percent change is ${(priceData.percentChange24h).toFixed(2)}%`, content: { symbol: content.symbol, cryptoName: content.cryptoName, From 75a8f823b1841d8c8fa84d0a68fc0bfbe110b7a3 Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 04:50:53 -0500 Subject: [PATCH 17/58] add coingecko api --- packages/plugin-coinmarketcap/README.md | 8 +-- .../src/actions/getPrice/index.ts | 9 ++-- .../src/actions/getPrice/service.ts | 51 ++++++++++++++++--- .../src/actions/getPrice/template.ts | 26 ++++++++++ .../plugin-coinmarketcap/src/environment.ts | 4 +- packages/plugin-coinmarketcap/src/index.ts | 2 +- 6 files changed, 83 insertions(+), 17 deletions(-) diff --git a/packages/plugin-coinmarketcap/README.md b/packages/plugin-coinmarketcap/README.md index 08e6294882..59c53a8833 100644 --- a/packages/plugin-coinmarketcap/README.md +++ b/packages/plugin-coinmarketcap/README.md @@ -18,12 +18,13 @@ npm install @elizaos/plugin-coinmarketcap ## Configuration -1. Get your API key from [CoinMarketCap](https://pro.coinmarketcap.com) +1. Get your API key from [CoinMarketCap](https://pro.coinmarketcap.com) or [CoinGecko](https://www.coingecko.com/en/api) (or fallback to CoinCap) 2. Set up your environment variables: ```bash COINMARKETCAP_API_KEY=your_api_key +COINGECKO_API_KEY=your_api_key ``` 3. Register the plugin in your Eliza configuration: @@ -118,10 +119,11 @@ CoinMarketCap API has different rate limits based on your subscription plan. Ple For support, please open an issue in the repository or reach out to the maintainers: -- Discord: 0xspit +- Discord: proteanx, 0xspit ## Links +- [CoinGecko API Documentation](https://www.coingecko.com/en/api) +- [CoinCap API Documentation](https://docs.coincap.io/) - [CoinMarketCap API Documentation](https://coinmarketcap.com/api/documentation/v1/) - - [GitHub Repository](https://github.com/elizaOS/eliza/tree/main/packages/plugin-coinmarketcap) diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts index 955f8656b6..5e4b3b6639 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts @@ -30,7 +30,7 @@ export default { // Always validate to true since we have a fallback API return true; }, - description: "Get the current price of a cryptocurrency from CoinMarketCap or CoinCap", + description: "Get the current price of a cryptocurrency from CoinGecko, CoinMarketCap, or CoinCap", handler: async ( runtime: IAgentRuntime, message: Memory, @@ -65,9 +65,10 @@ export default { throw new Error("Invalid price check content"); } - // Get API key if available - const apiKey = runtime.getSetting("COINMARKETCAP_API_KEY"); - const priceService = createPriceService(apiKey); + // Get API keys if available + const coingeckoApiKey = runtime.getSetting("COINGECKO_API_KEY"); + const coinmarketcapApiKey = runtime.getSetting("COINMARKETCAP_API_KEY"); + const priceService = createPriceService(coingeckoApiKey, coinmarketcapApiKey); try { const priceData = await priceService.getPrice( diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts index 5802103114..b560fc9b47 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts @@ -3,12 +3,21 @@ import { ApiResponse, PriceData } from "./types"; const COINMARKETCAP_BASE_URL = "https://pro-api.coinmarketcap.com/v1"; const COINCAP_BASE_URL = "https://api.coincap.io/v2"; +const COINGECKO_BASE_URL = "https://api.coingecko.com/api/v3"; -export const createPriceService = (apiKey?: string) => { - const coinmarketcapClient = apiKey ? axios.create({ +export const createPriceService = (coingeckoApiKey?: string, coinmarketcapApiKey?: string) => { + const coingeckoClient = coingeckoApiKey ? axios.create({ + baseURL: COINGECKO_BASE_URL, + headers: { + "x-cg-demo-api-key": coingeckoApiKey, + Accept: "application/json", + }, + }) : null; + + const coinmarketcapClient = coinmarketcapApiKey ? axios.create({ baseURL: COINMARKETCAP_BASE_URL, headers: { - "X-CMC_PRO_API_KEY": apiKey, + "X-CMC_PRO_API_KEY": coinmarketcapApiKey, Accept: "application/json", }, }) : null; @@ -30,8 +39,33 @@ export const createPriceService = (apiKey?: string) => { const normalizedCurrency = currency.toUpperCase().trim(); try { - if (coinmarketcapClient) { - // Try CoinMarketCap first if API key is available + // Try CoinGecko first if API key is available + if (coingeckoClient) { + const response = await coingeckoClient.get(`/simple/price`, { + params: { + ids: normalizedCrypto, + vs_currencies: normalizedCurrency.toLowerCase(), + include_market_cap: true, + include_24hr_vol: true, + include_24hr_change: true, + }, + }); + + const data = response.data[normalizedCrypto]; + if (!data) { + throw new Error(`No data found for cryptocurrency: ${normalizedCrypto}`); + } + + const currencyKey = normalizedCurrency.toLowerCase(); + return { + price: data[currencyKey], + marketCap: data[`${currencyKey}_market_cap`], + volume24h: data[`${currencyKey}_24h_vol`], + percentChange24h: data[`${currencyKey}_24h_change`], + }; + } + // Try CoinMarketCap if API key is available + else if (coinmarketcapClient) { const response = await coinmarketcapClient.get( "/cryptocurrency/quotes/latest", { @@ -62,9 +96,10 @@ export const createPriceService = (apiKey?: string) => { volume24h: quoteData.volume_24h, percentChange24h: quoteData.percent_change_24h, }; - } else { - // Fallback to CoinCap API - // CoinCap only supports USD, so we'll need to handle currency conversion differently + } + // Fallback to CoinCap API + else { + // CoinCap only supports USD if (normalizedCurrency !== "USD") { throw new Error("CoinCap API only supports USD currency"); } diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts index ec3be26389..3f1441cc77 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts +++ b/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts @@ -10,6 +10,32 @@ Here are the cryptocurrency symbol mappings: - polkadot/dot -> DOT (cryptoName: polkadot) - usdc -> USDC (cryptoName: usd-coin) - tether/usdt -> USDT (cryptoName: tether) +- shiba-inu/shib -> SHIB (cryptoName: shiba-inu) +- litecoin/ltc -> LTC (cryptoName: litecoin) +- bnb/bnb -> BNB (cryptoName: binance-smart-chain) +- avalanche/avax -> AVAX (cryptoName: avalanche) +- fantom/ftm -> FTM (cryptoName: fantom) +- optimism/op -> OP (cryptoName: optimism) +- arbitrum/arb -> ARB (cryptoName: arbitrum) +- polygon/matic -> MATIC (cryptoName: polygon) +- devault/dvt -> DVT (cryptoName: devault) +- bitcoin-cash/bch -> BCH (cryptoName: bitcoin-cash) +- litecoin/ltc -> LTC (cryptoName: litecoin) +- rune-pups/pups -> PUPS (cryptoName: pups) +- tron/trx -> TRX (cryptoName: tron) +- sui/sui -> SUI (cryptoName: sui) +- aptos/aptos -> APTOS (cryptoName: aptos) +- toncoin/ton -> TON (cryptoName: toncoin) +- tezos/xtz -> XTZ (cryptoName: tezos) +- kusama/ksm -> KSM (cryptoName: kusama) +- cosmos/atom -> ATOM (cryptoName: cosmos) +- filecoin/fil -> FIL (cryptoName: filecoin) +- stellar/xlm -> XLM (cryptoName: stellar) +- chainlink/link -> LINK (cryptoName: chainlink) +- nexa/nex -> NEX (cryptoName: nexa) +- kadena/kda -> KDA (cryptoName: kadena) +- kava/kava -> KAVA (cryptoName: kava) + IMPORTANT: Response must ALWAYS include "symbol", "cryptoName", and "currency" fields. diff --git a/packages/plugin-coinmarketcap/src/environment.ts b/packages/plugin-coinmarketcap/src/environment.ts index 33ca49f5f2..a4dd6ef381 100644 --- a/packages/plugin-coinmarketcap/src/environment.ts +++ b/packages/plugin-coinmarketcap/src/environment.ts @@ -2,6 +2,7 @@ import { IAgentRuntime } from "@elizaos/core"; import { z } from "zod"; export const coinmarketcapEnvSchema = z.object({ + COINGECKO_API_KEY: z.string().optional(), COINMARKETCAP_API_KEY: z.string().optional(), }); @@ -12,6 +13,7 @@ export async function validateCoinMarketCapConfig( ): Promise { try { const config = { + COINGECKO_API_KEY: runtime.getSetting("COINGECKO_API_KEY"), COINMARKETCAP_API_KEY: runtime.getSetting("COINMARKETCAP_API_KEY"), }; @@ -22,7 +24,7 @@ export async function validateCoinMarketCapConfig( .map((err) => `${err.path.join(".")}: ${err.message}`) .join("\n"); throw new Error( - `CoinMarketCap configuration validation failed:\n${errorMessages}` + `Configuration validation failed:\n${errorMessages}` ); } throw error; diff --git a/packages/plugin-coinmarketcap/src/index.ts b/packages/plugin-coinmarketcap/src/index.ts index 6b79825e0e..3188b82ac9 100644 --- a/packages/plugin-coinmarketcap/src/index.ts +++ b/packages/plugin-coinmarketcap/src/index.ts @@ -3,7 +3,7 @@ import getPrice from "./actions/getPrice"; export const coinmarketcapPlugin: Plugin = { name: "coinmarketcap", - description: "Plugin for cryptocurrency price checking using CoinMarketCap API with fallback to CoinCap API when no API key is provided", + description: "Plugin for cryptocurrency price checking using CoinGecko API (priority), CoinMarketCap API (fallback), or CoinCap API (free fallback) when no API keys are provided", actions: [getPrice], evaluators: [], providers: [], From 9ab81c1da7612703b6103f3350374af72bdfc1cf Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 05:15:59 -0500 Subject: [PATCH 18/58] use coinprice + add env variables --- .env.example | 7 +- agent/package.json | 2 +- agent/src/index.ts | 7 +- .../README.md | 19 +- .../package.json | 2 +- .../src/actions/getPrice/examples.ts | 0 .../src/actions/getPrice/index.ts | 1 - .../src/actions/getPrice/service.ts | 0 .../src/actions/getPrice/template.ts | 0 .../src/actions/getPrice/types.ts | 0 .../src/actions/getPrice/validation.ts | 0 .../src/environment.ts | 0 .../src/index.ts | 6 +- .../src/types.ts | 0 .../tsconfig.json | 0 .../tsup.config.ts | 0 pnpm-lock.yaml | 625 ++++-------------- 17 files changed, 165 insertions(+), 504 deletions(-) rename packages/{plugin-coinmarketcap => plugin-coinprice}/README.md (76%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/package.json (90%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/examples.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/index.ts (98%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/service.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/template.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/types.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/actions/getPrice/validation.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/environment.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/index.ts (75%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/src/types.ts (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/tsconfig.json (100%) rename packages/{plugin-coinmarketcap => plugin-coinprice}/tsup.config.ts (100%) diff --git a/.env.example b/.env.example index a01774e20d..67642ef8bd 100644 --- a/.env.example +++ b/.env.example @@ -256,9 +256,6 @@ CONFLUX_ESPACE_PRIVATE_KEY= CONFLUX_ESPACE_RPC_URL= CONFLUX_MEME_CONTRACT_ADDRESS= -# Coinmarketcap Configuration -COINMARKETCAP_API_KEY= - # ZeroG ZEROG_INDEXER_RPC= ZEROG_EVM_RPC= @@ -284,6 +281,10 @@ MEDIUM_VENICE_MODEL= # Default: llama-3.3-70b LARGE_VENICE_MODEL= # Default: llama-3.1-405b IMAGE_VENICE_MODEL= # Default: fluently-xl +# Coin Price Configuration +COINMARKETCAP_API_KEY= +COINGECKO_API_KEY= + # Akash Chat API Configuration docs: https://chatapi.akash.network/documentation AKASH_CHAT_API_KEY= # Get from https://chatapi.akash.network/ SMALL_AKASH_CHAT_API_MODEL= # Default: Meta-Llama-3-2-3B-Instruct diff --git a/agent/package.json b/agent/package.json index 99688494a0..4d192bf9bd 100644 --- a/agent/package.json +++ b/agent/package.json @@ -36,7 +36,7 @@ "@elizaos/plugin-bootstrap": "workspace:*", "@elizaos/plugin-intiface": "workspace:*", "@elizaos/plugin-coinbase": "workspace:*", - "@elizaos/plugin-coinmarketcap": "workspace:*", + "@elizaos/plugin-coinprice": "workspace:*", "@elizaos/plugin-conflux": "workspace:*", "@elizaos/plugin-evm": "workspace:*", "@elizaos/plugin-echochambers": "workspace:*", diff --git a/agent/src/index.ts b/agent/src/index.ts index 55e1e2257c..65207f9e5f 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -46,7 +46,7 @@ import { tradePlugin, webhookPlugin, } from "@elizaos/plugin-coinbase"; -import { coinmarketcapPlugin } from "@elizaos/plugin-coinmarketcap"; +import { coinPricePlugin } from "@elizaos/plugin-coinprice"; import { confluxPlugin } from "@elizaos/plugin-conflux"; import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; @@ -531,7 +531,7 @@ export async function createAgent( ? confluxPlugin : null, nodePlugin, - coinmarketcapPlugin, + coinPricePlugin, getSecret(character, "TAVILY_API_KEY") ? webSearchPlugin : null, getSecret(character, "SOLANA_PUBLIC_KEY") || (getSecret(character, "WALLET_PUBLIC_KEY") && @@ -559,9 +559,6 @@ export async function createAgent( ? nftGenerationPlugin : null, getSecret(character, "ZEROG_PRIVATE_KEY") ? zgPlugin : null, - getSecret(character, "COINMARKETCAP_API_KEY") - ? coinmarketcapPlugin - : null, getSecret(character, "COINBASE_COMMERCE_KEY") ? coinbaseCommercePlugin : null, diff --git a/packages/plugin-coinmarketcap/README.md b/packages/plugin-coinprice/README.md similarity index 76% rename from packages/plugin-coinmarketcap/README.md rename to packages/plugin-coinprice/README.md index 59c53a8833..7461afb69d 100644 --- a/packages/plugin-coinmarketcap/README.md +++ b/packages/plugin-coinprice/README.md @@ -1,4 +1,4 @@ -# @elizaos/plugin-coinmarketcap +# @elizaos/plugin-coinprice A plugin for Eliza that enables cryptocurrency price checking using the CoinMarketCap API. @@ -13,12 +13,12 @@ A plugin for Eliza that enables cryptocurrency price checking using the CoinMark ## Installation ```bash -npm install @elizaos/plugin-coinmarketcap +npm install @elizaos/plugin-coinprice ``` ## Configuration -1. Get your API key from [CoinMarketCap](https://pro.coinmarketcap.com) or [CoinGecko](https://www.coingecko.com/en/api) (or fallback to CoinCap) +1. Get your API key from [CoinGecko](https://www.coingecko.com/en/api) or [CoinMarketCap](https://pro.coinmarketcap.com) (or fallback to CoinCap) 2. Set up your environment variables: @@ -30,11 +30,11 @@ COINGECKO_API_KEY=your_api_key 3. Register the plugin in your Eliza configuration: ```typescript -import { CoinMarketCapPlugin } from "@elizaos/plugin-coinmarketcap"; +import { CoinPricePlugin } from "@elizaos/plugin-coinprice"; // In your Eliza configuration plugins: [ - new CoinMarketCapPlugin(), + new CoinPricePlugin(), // ... other plugins ]; ``` @@ -83,7 +83,8 @@ Fetches the current price of a cryptocurrency. | Variable | Description | Required | | --------------------- | -------------------------- | -------- | -| COINMARKETCAP_API_KEY | Your CoinMarketCap API key | Yes | +| COINMARKETCAP_API_KEY | Your CoinMarketCap API key | No | +| COINGECKO_API_KEY | Your CoinGecko API key | No | ### Types @@ -113,8 +114,12 @@ The plugin includes comprehensive error handling for: ## Rate Limits +CoinGecko API has different rate limits based on your subscription plan. Please refer to [CoinGecko's pricing page](https://www.coingecko.com/en/api) for detailed information. + CoinMarketCap API has different rate limits based on your subscription plan. Please refer to [CoinMarketCap's pricing page](https://coinmarketcap.com/api/pricing/) for detailed information. +CoinCap API has different rate limits based on your subscription plan. Please refer to [CoinCap's pricing page](https://coincap.io/api) for detailed information. + ## Support For support, please open an issue in the repository or reach out to the maintainers: @@ -126,4 +131,4 @@ For support, please open an issue in the repository or reach out to the maintain - [CoinGecko API Documentation](https://www.coingecko.com/en/api) - [CoinCap API Documentation](https://docs.coincap.io/) - [CoinMarketCap API Documentation](https://coinmarketcap.com/api/documentation/v1/) -- [GitHub Repository](https://github.com/elizaOS/eliza/tree/main/packages/plugin-coinmarketcap) +- [GitHub Repository](https://github.com/elizaOS/eliza/tree/main/packages/plugin-coinprice) diff --git a/packages/plugin-coinmarketcap/package.json b/packages/plugin-coinprice/package.json similarity index 90% rename from packages/plugin-coinmarketcap/package.json rename to packages/plugin-coinprice/package.json index 6dbdf24f1f..66638bc318 100644 --- a/packages/plugin-coinmarketcap/package.json +++ b/packages/plugin-coinprice/package.json @@ -1,5 +1,5 @@ { - "name": "@elizaos/plugin-coinmarketcap", + "name": "@elizaos/plugin-coinprice", "version": "0.1.7-alpha.2", "main": "dist/index.js", "type": "module", diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts b/packages/plugin-coinprice/src/actions/getPrice/examples.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/actions/getPrice/examples.ts rename to packages/plugin-coinprice/src/actions/getPrice/examples.ts diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts b/packages/plugin-coinprice/src/actions/getPrice/index.ts similarity index 98% rename from packages/plugin-coinmarketcap/src/actions/getPrice/index.ts rename to packages/plugin-coinprice/src/actions/getPrice/index.ts index 5e4b3b6639..951be9e220 100644 --- a/packages/plugin-coinmarketcap/src/actions/getPrice/index.ts +++ b/packages/plugin-coinprice/src/actions/getPrice/index.ts @@ -9,7 +9,6 @@ import { State, type Action, } from "@elizaos/core"; -import { validateCoinMarketCapConfig } from "../../environment"; import { priceExamples } from "./examples"; import { createPriceService } from "./service"; import { getPriceTemplate } from "./template"; diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/service.ts b/packages/plugin-coinprice/src/actions/getPrice/service.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/actions/getPrice/service.ts rename to packages/plugin-coinprice/src/actions/getPrice/service.ts diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/template.ts b/packages/plugin-coinprice/src/actions/getPrice/template.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/actions/getPrice/template.ts rename to packages/plugin-coinprice/src/actions/getPrice/template.ts diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/types.ts b/packages/plugin-coinprice/src/actions/getPrice/types.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/actions/getPrice/types.ts rename to packages/plugin-coinprice/src/actions/getPrice/types.ts diff --git a/packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts b/packages/plugin-coinprice/src/actions/getPrice/validation.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/actions/getPrice/validation.ts rename to packages/plugin-coinprice/src/actions/getPrice/validation.ts diff --git a/packages/plugin-coinmarketcap/src/environment.ts b/packages/plugin-coinprice/src/environment.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/environment.ts rename to packages/plugin-coinprice/src/environment.ts diff --git a/packages/plugin-coinmarketcap/src/index.ts b/packages/plugin-coinprice/src/index.ts similarity index 75% rename from packages/plugin-coinmarketcap/src/index.ts rename to packages/plugin-coinprice/src/index.ts index 3188b82ac9..4cdd269b3b 100644 --- a/packages/plugin-coinmarketcap/src/index.ts +++ b/packages/plugin-coinprice/src/index.ts @@ -1,12 +1,12 @@ import { Plugin } from "@elizaos/core"; import getPrice from "./actions/getPrice"; -export const coinmarketcapPlugin: Plugin = { - name: "coinmarketcap", +export const coinPricePlugin: Plugin = { + name: "coinprice", description: "Plugin for cryptocurrency price checking using CoinGecko API (priority), CoinMarketCap API (fallback), or CoinCap API (free fallback) when no API keys are provided", actions: [getPrice], evaluators: [], providers: [], }; -export default coinmarketcapPlugin; +export default coinPricePlugin; diff --git a/packages/plugin-coinmarketcap/src/types.ts b/packages/plugin-coinprice/src/types.ts similarity index 100% rename from packages/plugin-coinmarketcap/src/types.ts rename to packages/plugin-coinprice/src/types.ts diff --git a/packages/plugin-coinmarketcap/tsconfig.json b/packages/plugin-coinprice/tsconfig.json similarity index 100% rename from packages/plugin-coinmarketcap/tsconfig.json rename to packages/plugin-coinprice/tsconfig.json diff --git a/packages/plugin-coinmarketcap/tsup.config.ts b/packages/plugin-coinprice/tsup.config.ts similarity index 100% rename from packages/plugin-coinmarketcap/tsup.config.ts rename to packages/plugin-coinprice/tsup.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42845ec1c5..eb06fd4c1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -159,9 +159,9 @@ importers: '@elizaos/plugin-coinbase': specifier: workspace:* version: link:../packages/plugin-coinbase - '@elizaos/plugin-coinmarketcap': + '@elizaos/plugin-coinprice': specifier: workspace:* - version: link:../packages/plugin-coinmarketcap + version: link:../packages/plugin-coinprice '@elizaos/plugin-conflux': specifier: workspace:* version: link:../packages/plugin-conflux @@ -249,16 +249,16 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + version: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)))(typescript@5.6.3) ts-node: specifier: 10.9.2 - version: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3) + version: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) client: dependencies: @@ -371,6 +371,9 @@ importers: '@docusaurus/preset-classic': specifier: 3.6.3 version: 3.6.3(@algolia/client-search@5.18.0)(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/react@18.3.12)(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@docusaurus/theme-common': + specifier: 3.6.3 + version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) '@docusaurus/theme-mermaid': specifier: 3.6.3 version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10) @@ -386,6 +389,9 @@ importers: dotenv: specifier: ^16.4.7 version: 16.4.7 + lunr: + specifier: 2.3.9 + version: 2.3.9 prism-react-renderer: specifier: 2.3.1 version: 2.3.1(react@18.3.1) @@ -429,7 +435,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/adapter-redis: dependencies: @@ -448,7 +454,7 @@ importers: version: 5.0.0 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/adapter-sqlite: dependencies: @@ -470,7 +476,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/adapter-sqljs: dependencies: @@ -492,7 +498,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/adapter-supabase: dependencies: @@ -508,7 +514,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-auto: dependencies: @@ -539,7 +545,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-direct: dependencies: @@ -582,7 +588,7 @@ importers: version: 1.4.12 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-discord: dependencies: @@ -619,7 +625,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-farcaster: dependencies: @@ -632,7 +638,7 @@ importers: devDependencies: tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-github: dependencies: @@ -657,7 +663,7 @@ importers: version: 8.1.0 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-lens: dependencies: @@ -676,7 +682,7 @@ importers: devDependencies: tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-slack: dependencies: @@ -734,7 +740,7 @@ importers: version: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@18.19.69)(typescript@5.6.3) tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) typescript: specifier: ^5.0.0 version: 5.6.3 @@ -756,7 +762,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/client-twitter: dependencies: @@ -778,7 +784,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/core: dependencies: @@ -947,7 +953,7 @@ importers: version: 2.8.1 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) typescript: specifier: 5.6.3 version: 5.6.3 @@ -984,7 +990,7 @@ importers: version: 6.13.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-3d-generation: dependencies: @@ -993,7 +999,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1005,7 +1011,7 @@ importers: version: link:../core tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) web3: specifier: ^4.15.0 version: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -1035,7 +1041,7 @@ importers: version: 5.1.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1054,7 +1060,7 @@ importers: devDependencies: tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-bootstrap: dependencies: @@ -1063,7 +1069,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1094,9 +1100,9 @@ importers: version: 20.17.9 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) - packages/plugin-coinmarketcap: + packages/plugin-coinprice: dependencies: '@elizaos/core': specifier: workspace:* @@ -1110,7 +1116,7 @@ importers: devDependencies: tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-conflux: dependencies: @@ -1131,7 +1137,7 @@ importers: version: link:../plugin-trustdb tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) web3: specifier: ^4.15.0 version: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -1170,30 +1176,11 @@ importers: version: 16.3.0 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 - packages/plugin-ferePro: - dependencies: - '@elizaos/core': - specifier: ^0.1.7-alpha.1 - version: 0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1) - tsup: - specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) - ws: - specifier: ^8.18.0 - version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - devDependencies: - '@types/ws': - specifier: ^8.5.13 - version: 8.5.13 - tsx: - specifier: ^4.19.2 - version: 4.19.2 - packages/plugin-flow: dependencies: '@elizaos/core': @@ -1204,7 +1191,7 @@ importers: version: 1.5.1 '@onflow/fcl': specifier: 1.13.1 - version: 1.13.1(@types/react@18.3.12)(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(tsx@4.19.2)(utf-8-validate@5.0.10) + version: 1.13.1(@types/react@18.3.12)(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(utf-8-validate@5.0.10) '@onflow/typedefs': specifier: 1.4.0 version: 1.4.0 @@ -1241,7 +1228,7 @@ importers: version: 10.0.0 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1259,7 +1246,7 @@ importers: version: 0.97.2(encoding@0.1.13)(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1274,7 +1261,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-goat: dependencies: @@ -1283,7 +1270,7 @@ importers: version: link:../core '@goat-sdk/adapter-vercel-ai': specifier: 0.2.0 - version: 0.2.0(@goat-sdk/core@0.4.0)(ai@3.4.33(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8)) + version: 0.2.0(@goat-sdk/core@0.4.0)(ai@3.4.33(openai@4.77.3(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8)) '@goat-sdk/core': specifier: 0.4.0 version: 0.4.0 @@ -1301,7 +1288,7 @@ importers: version: 0.2.0(@goat-sdk/wallet-evm@0.2.0(@goat-sdk/core@0.4.0)(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.21.53(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) viem: specifier: 2.21.53 version: 2.21.53(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -1332,10 +1319,10 @@ importers: version: 29.5.14 jest: specifier: 29.7.0 - version: 29.7.0(@types/node@22.10.5) + version: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) typescript: specifier: 5.6.3 version: 5.6.3 @@ -1347,7 +1334,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1365,7 +1352,7 @@ importers: version: 1.0.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1392,7 +1379,7 @@ importers: version: 2.1.1 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.5 version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1422,7 +1409,7 @@ importers: version: 5.1.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1467,7 +1454,7 @@ importers: version: 5.1.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1476,10 +1463,10 @@ importers: dependencies: '@aws-sdk/client-s3': specifier: ^3.705.0 - version: 3.721.0 + version: 3.722.0 '@aws-sdk/s3-request-presigner': specifier: ^3.705.0 - version: 3.721.0 + version: 3.722.0 '@cliqz/adblocker-playwright': specifier: 1.34.0 version: 1.34.0(playwright@1.48.2) @@ -1648,7 +1635,7 @@ importers: version: 22.8.4 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-solana: dependencies: @@ -1693,7 +1680,7 @@ importers: version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.29.1)(typescript@5.6.3)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1723,7 +1710,7 @@ importers: version: 6.18.0(encoding@0.1.13) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) unruggable-sdk: specifier: 1.4.0 version: 1.4.0(starknet@6.18.0(encoding@0.1.13)) @@ -1750,7 +1737,7 @@ importers: version: 1.2.0-rc.3(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1784,7 +1771,7 @@ importers: version: 5.1.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) vitest: specifier: 2.1.4 version: 2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) @@ -1823,7 +1810,7 @@ importers: version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.29.1)(typescript@5.6.3)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1853,7 +1840,7 @@ importers: version: 5.1.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1868,7 +1855,7 @@ importers: version: 3.2.2 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) uuid: specifier: 11.0.3 version: 11.0.3 @@ -1893,7 +1880,7 @@ importers: version: 0.0.17 tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) packages/plugin-video-generation: dependencies: @@ -1902,7 +1889,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1914,7 +1901,7 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -1942,10 +1929,10 @@ importers: version: 8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3) jest: specifier: 29.7.0 - version: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + version: 29.7.0(@types/node@20.17.9) ts-jest: specifier: 29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.9))(typescript@5.6.3) typescript: specifier: 5.6.3 version: 5.6.3 @@ -1960,7 +1947,7 @@ importers: version: link:../plugin-trustdb tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0) web3: specifier: ^4.15.0 version: 4.16.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) @@ -2325,8 +2312,8 @@ packages: resolution: {integrity: sha512-SCTx9DKOnfEKyWb6bx5J7aeowBig8QmiqOJlE0sMM/pbpF70YGC/ugk1/yFJAJlAkoDadtRvseFpwLvrg7N73Q==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-s3@3.721.0': - resolution: {integrity: sha512-uCZC8elYhUFF21yq1yB5TrE/VYz8A4/VnttUHc65/jqnHReTDvEC0XAc756tJnjfrReyM1ws12FzBLHoW/NDjg==} + '@aws-sdk/client-s3@3.722.0': + resolution: {integrity: sha512-FttdkB39TKjqEITfZJcs6Ihh6alICsNEne0ouLvh8re+gAuTK96zWcfX22mP5ap1QEsATaOGRNsMnyfsDSM0zw==} engines: {node: '>=16.0.0'} '@aws-sdk/client-sso-oidc@3.721.0': @@ -2443,8 +2430,8 @@ packages: resolution: {integrity: sha512-HJzsQxgMOAzZrbf/YIqEx30or4tZK1oNAk6Wm6xecUQx+23JXIaePRu1YFUOLBBERQ4QBPpISFurZWBMZ5ibAw==} engines: {node: '>=16.0.0'} - '@aws-sdk/s3-request-presigner@3.721.0': - resolution: {integrity: sha512-2ibKGssj2TAQyfthNihhBqWdwowlol9bDpKybIi2T6D8l2L9g0ENGLNE50MYzSFAQ3LcjzcvLQ/GByRPiuK+pQ==} + '@aws-sdk/s3-request-presigner@3.722.0': + resolution: {integrity: sha512-dh4yYywf3tHCUwIU8eAQdoFXsjWcssoQXTKoqaqwqRh4WxwuaiRiw6dmD0Q5m6sPsLiHrTPemmDXsF4mLdjmsQ==} engines: {node: '>=16.0.0'} '@aws-sdk/signature-v4-multi-region@3.716.0': @@ -3890,9 +3877,6 @@ packages: peerDependencies: onnxruntime-node: 1.20.1 - '@elizaos/core@0.1.7-alpha.2': - resolution: {integrity: sha512-gNvFw/Xnv4dlcfmmKxRa+baKq6en4TitAjUGvo8LgAUkSk156A0fffJ0lAsc1rX8zMB5NsIqdvMCbwKxDd54OQ==} - '@emnapi/core@1.3.1': resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} @@ -3918,12 +3902,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.24.2': resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} @@ -3942,12 +3920,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.24.2': resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} @@ -3966,12 +3938,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.24.2': resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} @@ -3990,12 +3956,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.24.2': resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} @@ -4014,12 +3974,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.24.2': resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} @@ -4038,12 +3992,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.24.2': resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} @@ -4062,12 +4010,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.24.2': resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} @@ -4086,12 +4028,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} @@ -4110,12 +4046,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.24.2': resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} @@ -4134,12 +4064,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.24.2': resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} @@ -4158,12 +4082,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.24.2': resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} @@ -4182,12 +4100,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.24.2': resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} @@ -4206,12 +4118,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.24.2': resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} @@ -4230,12 +4136,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.24.2': resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} @@ -4254,12 +4154,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.24.2': resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} @@ -4278,12 +4172,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.24.2': resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} @@ -4302,12 +4190,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.24.2': resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} @@ -4332,24 +4214,12 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.24.2': resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} @@ -4368,12 +4238,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} @@ -4392,12 +4256,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.24.2': resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} @@ -4416,12 +4274,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.24.2': resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} @@ -4440,12 +4292,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.24.2': resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} @@ -4464,12 +4310,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.24.2': resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} @@ -9160,8 +9000,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.5.0: - resolution: {integrity: sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==} + bare-events@2.5.1: + resolution: {integrity: sha512-Bw2PgKSrZ3uCuSV9WQ998c/GTJTd+9bWj97n7aDQMP8dP/exAZQlJeswPty0ISy+HZD+9Ex+C7CCnc9Q5QJFmQ==} bare-fs@2.3.5: resolution: {integrity: sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==} @@ -11234,11 +11074,6 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.24.2: resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} @@ -12009,9 +11844,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - get-uri@6.0.4: resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} engines: {node: '>= 14'} @@ -15007,8 +14839,8 @@ packages: zod: optional: true - openai@4.77.0: - resolution: {integrity: sha512-WWacavtns/7pCUkOWvQIjyOfcdr9X+9n9Vvb0zFeKVDAqwCMDHB+iSr24SVaBAhplvSG6JrRXFpcNM9gWhOGIw==} + openai@4.77.3: + resolution: {integrity: sha512-wLDy4+KWHz31HRFMW2+9KQuVuT2QWhs0z94w1Gm1h2Ut9vIHr9/rHZggbykZEfyiaJRVgw8ZS9K6AylDWzvPYw==} hasBin: true peerDependencies: zod: ^3.23.8 @@ -16955,9 +16787,6 @@ packages: resolve-pathname@3.0.0: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} @@ -18267,11 +18096,6 @@ packages: typescript: optional: true - tsx@4.19.2: - resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} - engines: {node: '>=18.0.0'} - hasBin: true - tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} @@ -20113,7 +19937,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-s3@3.721.0': + '@aws-sdk/client-s3@3.722.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 @@ -20606,7 +20430,7 @@ snapshots: '@smithy/util-middleware': 3.0.11 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.721.0': + '@aws-sdk/s3-request-presigner@3.722.0': dependencies: '@aws-sdk/signature-v4-multi-region': 3.716.0 '@aws-sdk/types': 3.714.0 @@ -23035,56 +22859,6 @@ snapshots: '@huggingface/jinja': 0.2.2 onnxruntime-node: 1.20.1 - '@elizaos/core@0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)': - dependencies: - '@ai-sdk/anthropic': 0.0.56(zod@3.23.8) - '@ai-sdk/google': 0.0.55(zod@3.23.8) - '@ai-sdk/google-vertex': 0.0.43(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(zod@3.23.8) - '@ai-sdk/groq': 0.0.3(zod@3.23.8) - '@ai-sdk/openai': 1.0.5(zod@3.23.8) - '@anthropic-ai/sdk': 0.30.1(encoding@0.1.13) - '@fal-ai/client': 1.2.0 - '@types/uuid': 10.0.0 - ai: 3.4.33(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8) - anthropic-vertex-ai: 1.0.2(encoding@0.1.13)(zod@3.23.8) - fastembed: 1.14.1 - fastestsmallesttextencoderdecoder: 1.0.22 - gaxios: 6.7.1(encoding@0.1.13) - glob: 11.0.0 - handlebars: 4.7.8 - js-sha1: 0.7.0 - js-tiktoken: 1.0.15 - langchain: 0.3.6(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) - ollama-ai-provider: 0.16.1(zod@3.23.8) - openai: 4.73.0(encoding@0.1.13)(zod@3.23.8) - tinyld: 1.3.4 - together-ai: 0.7.0(encoding@0.1.13) - unique-names-generator: 4.7.1 - uuid: 11.0.3 - zod: 3.23.8 - transitivePeerDependencies: - - '@google-cloud/vertexai' - - '@langchain/anthropic' - - '@langchain/aws' - - '@langchain/cohere' - - '@langchain/core' - - '@langchain/google-genai' - - '@langchain/google-vertexai' - - '@langchain/groq' - - '@langchain/mistralai' - - '@langchain/ollama' - - axios - - cheerio - - encoding - - peggy - - react - - solid-js - - sswr - - supports-color - - svelte - - typeorm - - vue - '@emnapi/core@1.3.1': dependencies: '@emnapi/wasi-threads': 1.0.1 @@ -23110,9 +22884,6 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.23.1': - optional: true - '@esbuild/aix-ppc64@0.24.2': optional: true @@ -23122,9 +22893,6 @@ snapshots: '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.23.1': - optional: true - '@esbuild/android-arm64@0.24.2': optional: true @@ -23134,9 +22902,6 @@ snapshots: '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.23.1': - optional: true - '@esbuild/android-arm@0.24.2': optional: true @@ -23146,9 +22911,6 @@ snapshots: '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.23.1': - optional: true - '@esbuild/android-x64@0.24.2': optional: true @@ -23158,9 +22920,6 @@ snapshots: '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.23.1': - optional: true - '@esbuild/darwin-arm64@0.24.2': optional: true @@ -23170,9 +22929,6 @@ snapshots: '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.23.1': - optional: true - '@esbuild/darwin-x64@0.24.2': optional: true @@ -23182,9 +22938,6 @@ snapshots: '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.23.1': - optional: true - '@esbuild/freebsd-arm64@0.24.2': optional: true @@ -23194,9 +22947,6 @@ snapshots: '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.23.1': - optional: true - '@esbuild/freebsd-x64@0.24.2': optional: true @@ -23206,9 +22956,6 @@ snapshots: '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.23.1': - optional: true - '@esbuild/linux-arm64@0.24.2': optional: true @@ -23218,9 +22965,6 @@ snapshots: '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.23.1': - optional: true - '@esbuild/linux-arm@0.24.2': optional: true @@ -23230,9 +22974,6 @@ snapshots: '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.23.1': - optional: true - '@esbuild/linux-ia32@0.24.2': optional: true @@ -23242,9 +22983,6 @@ snapshots: '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.23.1': - optional: true - '@esbuild/linux-loong64@0.24.2': optional: true @@ -23254,9 +22992,6 @@ snapshots: '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.23.1': - optional: true - '@esbuild/linux-mips64el@0.24.2': optional: true @@ -23266,9 +23001,6 @@ snapshots: '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.23.1': - optional: true - '@esbuild/linux-ppc64@0.24.2': optional: true @@ -23278,9 +23010,6 @@ snapshots: '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.23.1': - optional: true - '@esbuild/linux-riscv64@0.24.2': optional: true @@ -23290,9 +23019,6 @@ snapshots: '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.23.1': - optional: true - '@esbuild/linux-s390x@0.24.2': optional: true @@ -23302,9 +23028,6 @@ snapshots: '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.23.1': - optional: true - '@esbuild/linux-x64@0.24.2': optional: true @@ -23317,15 +23040,9 @@ snapshots: '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.23.1': - optional: true - '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/openbsd-arm64@0.23.1': - optional: true - '@esbuild/openbsd-arm64@0.24.2': optional: true @@ -23335,9 +23052,6 @@ snapshots: '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.23.1': - optional: true - '@esbuild/openbsd-x64@0.24.2': optional: true @@ -23347,9 +23061,6 @@ snapshots: '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.23.1': - optional: true - '@esbuild/sunos-x64@0.24.2': optional: true @@ -23359,9 +23070,6 @@ snapshots: '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.23.1': - optional: true - '@esbuild/win32-arm64@0.24.2': optional: true @@ -23371,9 +23079,6 @@ snapshots: '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.23.1': - optional: true - '@esbuild/win32-ia32@0.24.2': optional: true @@ -23383,9 +23088,6 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.23.1': - optional: true - '@esbuild/win32-x64@0.24.2': optional: true @@ -23963,10 +23665,10 @@ snapshots: '@fuels/vm-asm@0.58.2': {} - '@goat-sdk/adapter-vercel-ai@0.2.0(@goat-sdk/core@0.4.0)(ai@3.4.33(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8))': + '@goat-sdk/adapter-vercel-ai@0.2.0(@goat-sdk/core@0.4.0)(ai@3.4.33(openai@4.77.3(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8))': dependencies: '@goat-sdk/core': 0.4.0 - ai: 3.4.33(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8) + ai: 3.4.33(openai@4.77.3(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8) zod: 3.23.8 '@goat-sdk/core@0.4.0': @@ -24246,7 +23948,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -24260,7 +23962,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -24494,7 +24196,7 @@ snapshots: dependencies: '@langchain/core': 0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) js-tiktoken: 1.0.15 - openai: 4.77.0(encoding@0.1.13)(zod@3.23.8) + openai: 4.77.3(encoding@0.1.13)(zod@3.23.8) zod: 3.23.8 zod-to-json-schema: 3.24.1(zod@3.23.8) transitivePeerDependencies: @@ -26058,7 +25760,7 @@ snapshots: - supports-color - utf-8-validate - '@onflow/fcl-wc@5.5.1(@onflow/fcl-core@1.13.1(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(tsx@4.19.2)(utf-8-validate@5.0.10)': + '@onflow/fcl-wc@5.5.1(@onflow/fcl-core@1.13.1(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.26.0 '@onflow/config': 1.5.1 @@ -26070,7 +25772,7 @@ snapshots: '@walletconnect/sign-client': 2.17.3(bufferutil@4.0.9)(ioredis@5.4.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.17.3(ioredis@5.4.2) '@walletconnect/utils': 2.17.3(ioredis@5.4.2) - postcss-cli: 11.0.0(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2) + postcss-cli: 11.0.0(jiti@2.4.2)(postcss@8.4.49) preact: 10.25.4 tailwindcss: 3.4.15(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)) transitivePeerDependencies: @@ -26103,12 +25805,12 @@ snapshots: - uploadthing - utf-8-validate - '@onflow/fcl@1.13.1(@types/react@18.3.12)(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(tsx@4.19.2)(utf-8-validate@5.0.10)': + '@onflow/fcl@1.13.1(@types/react@18.3.12)(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.26.0 '@onflow/config': 1.5.1 '@onflow/fcl-core': 1.13.1(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10) - '@onflow/fcl-wc': 5.5.1(@onflow/fcl-core@1.13.1(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(tsx@4.19.2)(utf-8-validate@5.0.10) + '@onflow/fcl-wc': 5.5.1(@onflow/fcl-core@1.13.1(bufferutil@4.0.9)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(ioredis@5.4.2)(jiti@2.4.2)(postcss@8.4.49)(react@18.3.1)(utf-8-validate@5.0.10) '@onflow/interaction': 0.0.11 '@onflow/rlp': 1.2.3 '@onflow/sdk': 1.5.5(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -29769,7 +29471,7 @@ snapshots: - solid-js - vue - ai@3.4.33(openai@4.77.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8): + ai@3.4.33(openai@4.77.3(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.26 '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) @@ -29785,7 +29487,7 @@ snapshots: secure-json-parse: 2.7.0 zod-to-json-schema: 3.24.1(zod@3.23.8) optionalDependencies: - openai: 4.77.0(encoding@0.1.13)(zod@3.23.8) + openai: 4.77.3(encoding@0.1.13)(zod@3.23.8) react: 18.3.1 sswr: 2.1.0(svelte@5.16.1) svelte: 5.16.1 @@ -30298,12 +30000,12 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.5.0: + bare-events@2.5.1: optional: true bare-fs@2.3.5: dependencies: - bare-events: 2.5.0 + bare-events: 2.5.1 bare-path: 2.1.3 bare-stream: 2.6.1 optional: true @@ -31599,13 +31301,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): + create-jest@29.7.0(@types/node@20.17.9): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -31614,13 +31316,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.10.5): + create-jest@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.5) + jest-config: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -32887,33 +32589,6 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - esbuild@0.24.2: optionalDependencies: '@esbuild/aix-ppc64': 0.24.2 @@ -33441,7 +33116,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.3.4 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -33989,10 +33664,6 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.7 - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 - get-uri@6.0.4: dependencies: basic-ftp: 5.0.5 @@ -35477,16 +35148,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): + jest-cli@29.7.0(@types/node@20.17.9): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + create-jest: 29.7.0(@types/node@20.17.9) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -35496,16 +35167,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.10.5): + jest-cli@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.5) + create-jest: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.10.5) + jest-config: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -35596,7 +35267,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -35622,7 +35293,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.17.9 - ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3) + ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -35658,7 +35329,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@22.10.5): + jest-config@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 '@jest/test-sequencer': 29.7.0 @@ -35684,6 +35355,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.10.5 + ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -35952,24 +35624,24 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): + jest@29.7.0(@types/node@20.17.9): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + jest-cli: 29.7.0(@types/node@20.17.9) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest@29.7.0(@types/node@22.10.5): + jest@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.10.5) + jest-cli: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -38316,7 +37988,7 @@ snapshots: transitivePeerDependencies: - encoding - openai@4.77.0(encoding@0.1.13)(zod@3.23.8): + openai@4.77.3(encoding@0.1.13)(zod@3.23.8): dependencies: '@types/node': 18.19.69 '@types/node-fetch': 2.6.12 @@ -38987,7 +38659,7 @@ snapshots: postcss: 8.4.49 postcss-value-parser: 4.2.0 - postcss-cli@11.0.0(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2): + postcss-cli@11.0.0(jiti@2.4.2)(postcss@8.4.49): dependencies: chokidar: 3.6.0 dependency-graph: 0.11.0 @@ -38996,7 +38668,7 @@ snapshots: globby: 14.0.2 picocolors: 1.1.1 postcss: 8.4.49 - postcss-load-config: 5.1.0(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2) + postcss-load-config: 5.1.0(jiti@2.4.2)(postcss@8.4.49) postcss-reporter: 7.1.0(postcss@8.4.49) pretty-hrtime: 1.0.3 read-cache: 1.0.0 @@ -39183,22 +38855,20 @@ snapshots: postcss: 8.4.49 ts-node: 10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3) - postcss-load-config@5.1.0(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2): + postcss-load-config@5.1.0(jiti@2.4.2)(postcss@8.4.49): dependencies: lilconfig: 3.1.3 yaml: 2.7.0 optionalDependencies: jiti: 2.4.2 postcss: 8.4.49 - tsx: 4.19.2 - postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.7.0): + postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.4.49)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.4.2 postcss: 8.4.49 - tsx: 4.19.2 yaml: 2.7.0 postcss-loader@7.3.4(postcss@8.4.49)(typescript@5.6.3)(webpack@5.97.1(@swc/core@1.10.4(@swc/helpers@0.5.15))): @@ -40564,8 +40234,6 @@ snapshots: resolve-pathname@3.0.0: {} - resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.3: {} resolve@1.17.0: @@ -41433,7 +41101,7 @@ snapshots: queue-tick: 1.0.1 text-decoder: 1.2.3 optionalDependencies: - bare-events: 2.5.0 + bare-events: 2.5.1 strict-uri-encode@2.0.0: {} @@ -41988,12 +41656,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) + jest: 29.7.0(@types/node@22.10.5)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -42027,6 +41695,25 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.0) + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.9))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.17.9) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 @@ -42068,26 +41755,6 @@ snapshots: optionalDependencies: '@swc/core': 1.10.4(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.17.9 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.6.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.10.4(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.10.5)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -42107,7 +41774,6 @@ snapshots: yn: 3.1.1 optionalDependencies: '@swc/core': 1.10.4(@swc/helpers@0.5.15) - optional: true ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3): dependencies: @@ -42149,7 +41815,7 @@ snapshots: tsscmp@1.0.6: {} - tsup@8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0): + tsup@8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.7.0): dependencies: bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 @@ -42159,7 +41825,7 @@ snapshots: esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.7.0) + postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.4.49)(yaml@2.7.0) resolve-from: 5.0.0 rollup: 4.29.1 source-map: 0.8.0-beta.0 @@ -42177,13 +41843,6 @@ snapshots: - tsx - yaml - tsx@4.19.2: - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 - optionalDependencies: - fsevents: 2.3.3 - tty-browserify@0.0.1: {} tuf-js@2.2.1: From 2a5e8d62ca83533d4f56548acfe61b3a9f4b3c3d Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 05:30:06 -0500 Subject: [PATCH 19/58] clean-up for current develop --- agent/src/index.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/agent/src/index.ts b/agent/src/index.ts index 65207f9e5f..bcc5237d74 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -1,5 +1,4 @@ import { PostgresDatabaseAdapter } from "@elizaos/adapter-postgres"; -import { RedisClient } from "@elizaos/adapter-redis"; import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite"; import { AutoClientInterface } from "@elizaos/client-auto"; import { DiscordClientInterface } from "@elizaos/client-discord"; @@ -11,33 +10,31 @@ import { TwitterClientInterface } from "@elizaos/client-twitter"; import { AgentRuntime, CacheManager, - CacheStore, Character, - Client, Clients, DbCacheAdapter, defaultCharacter, elizaLogger, FsCacheAdapter, IAgentRuntime, - ICacheManager, IDatabaseAdapter, IDatabaseCacheAdapter, ModelProviderName, settings, stringToUuid, validateCharacterConfig, + CacheStore, + Client, + ICacheManager, + parseBooleanFromText, } from "@elizaos/core"; +import { RedisClient } from "@elizaos/adapter-redis"; import { zgPlugin } from "@elizaos/plugin-0g"; - import { bootstrapPlugin } from "@elizaos/plugin-bootstrap"; import createGoatPlugin from "@elizaos/plugin-goat"; // import { intifacePlugin } from "@elizaos/plugin-intiface"; import { DirectClient } from "@elizaos/client-direct"; -import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; -import { abstractPlugin } from "@elizaos/plugin-abstract"; import { aptosPlugin } from "@elizaos/plugin-aptos"; -import { avalanchePlugin } from "@elizaos/plugin-avalanche"; import { advancedTradePlugin, coinbaseCommercePlugin, @@ -48,30 +45,32 @@ import { } from "@elizaos/plugin-coinbase"; import { coinPricePlugin } from "@elizaos/plugin-coinprice"; import { confluxPlugin } from "@elizaos/plugin-conflux"; -import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; -import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; import { evmPlugin } from "@elizaos/plugin-evm"; +import { storyPlugin } from "@elizaos/plugin-story"; import { flowPlugin } from "@elizaos/plugin-flow"; import { fuelPlugin } from "@elizaos/plugin-fuel"; - import { imageGenerationPlugin } from "@elizaos/plugin-image-generation"; +import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; import { multiversxPlugin } from "@elizaos/plugin-multiversx"; import { nearPlugin } from "@elizaos/plugin-near"; import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation"; import { createNodePlugin } from "@elizaos/plugin-node"; import { solanaPlugin } from "@elizaos/plugin-solana"; -import { storyPlugin } from "@elizaos/plugin-story"; import { suiPlugin } from "@elizaos/plugin-sui"; import { TEEMode, teePlugin } from "@elizaos/plugin-tee"; import { tonPlugin } from "@elizaos/plugin-ton"; -import { webSearchPlugin } from "@elizaos/plugin-web-search"; import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; +import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; +import { abstractPlugin } from "@elizaos/plugin-abstract"; +import { avalanchePlugin } from "@elizaos/plugin-avalanche"; +import { webSearchPlugin } from "@elizaos/plugin-web-search"; +import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; import Database from "better-sqlite3"; import fs from "fs"; -import net from "net"; import path from "path"; import { fileURLToPath } from "url"; import yargs from "yargs"; +import net from "net"; const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file const __dirname = path.dirname(__filename); // get the name of the directory From 48ea3f346a3d32e9e00b5426063afe8ec7b2510e Mon Sep 17 00:00:00 2001 From: KONFeature Date: Sat, 4 Jan 2025 02:20:39 +0100 Subject: [PATCH 20/58] feat: wip pglite --- docs/docs/packages/adapters.md | 36 + packages/adapter-pglite/.npmignore | 6 + packages/adapter-pglite/eslint.config.mjs | 3 + packages/adapter-pglite/package.json | 36 + packages/adapter-pglite/schema.sql | 140 +++ packages/adapter-pglite/src/index.ts | 1355 +++++++++++++++++++++ packages/adapter-pglite/tsconfig.json | 11 + packages/adapter-pglite/tsup.config.ts | 22 + pnpm-lock.yaml | 21 + 9 files changed, 1630 insertions(+) create mode 100644 packages/adapter-pglite/.npmignore create mode 100644 packages/adapter-pglite/eslint.config.mjs create mode 100644 packages/adapter-pglite/package.json create mode 100644 packages/adapter-pglite/schema.sql create mode 100644 packages/adapter-pglite/src/index.ts create mode 100644 packages/adapter-pglite/tsconfig.json create mode 100644 packages/adapter-pglite/tsup.config.ts diff --git a/docs/docs/packages/adapters.md b/docs/docs/packages/adapters.md index cce1e5e5ff..82be60af69 100644 --- a/docs/docs/packages/adapters.md +++ b/docs/docs/packages/adapters.md @@ -78,10 +78,17 @@ classDiagram +inMemoryOperations() } + class PGLiteDatabaseAdapter { + -db: PGlite + +searchMemoriesByEmbedding() + +createMemory() + } + DatabaseAdapter <|-- PostgresDatabaseAdapter DatabaseAdapter <|-- SqliteDatabaseAdapter DatabaseAdapter <|-- SupabaseDatabaseAdapter DatabaseAdapter <|-- SqlJsDatabaseAdapter + DatabaseAdapter <|-- PgLiteDatabaseAdapter class AgentRuntime { -databaseAdapter: DatabaseAdapter @@ -149,6 +156,9 @@ pnpm add @elizaos/adapter-sqljs sql.js # Supabase pnpm add @elizaos/adapter-supabase @supabase/supabase-js + +# PgLite +pnpm add @elizaos/adapter-pglite @electric-sql/pglite ``` --- @@ -198,6 +208,32 @@ const db = new SupabaseDatabaseAdapter( ); ``` +```typescript +import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite"; +import Database from "better-sqlite3"; + +const db = new SqliteDatabaseAdapter( + new Database("./db.sqlite", { + // SQLite options + memory: false, + readonly: false, + fileMustExist: false, + }), +); +``` + +### PgLite Setup + +```typescript +import { PGLiteDatabaseAdapter } from "@elizaos/adapter-pglite"; + +const db = new PGLiteDatabaseAdapter( + new PGLite({ + dataDir: "./db" + }) +); +``` + --- ## Core Features diff --git a/packages/adapter-pglite/.npmignore b/packages/adapter-pglite/.npmignore new file mode 100644 index 0000000000..078562ecea --- /dev/null +++ b/packages/adapter-pglite/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/adapter-pglite/eslint.config.mjs b/packages/adapter-pglite/eslint.config.mjs new file mode 100644 index 0000000000..92fe5bbebe --- /dev/null +++ b/packages/adapter-pglite/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/adapter-pglite/package.json b/packages/adapter-pglite/package.json new file mode 100644 index 0000000000..7f7167333e --- /dev/null +++ b/packages/adapter-pglite/package.json @@ -0,0 +1,36 @@ +{ + "name": "@elizaos/adapter-pglite", + "version": "0.1.7-alpha.2", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "@elizaos/source": "./src/index.ts", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "files": [ + "dist" + ], + "dependencies": { + "@electric-sql/pglite": "^0.2.15", + "@elizaos/core": "workspace:*" + }, + "devDependencies": { + "tsup": "8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache ." + }, + "peerDependencies": { + "whatwg-url": "7.1.0" + } +} diff --git a/packages/adapter-pglite/schema.sql b/packages/adapter-pglite/schema.sql new file mode 100644 index 0000000000..4a0f7c6f1d --- /dev/null +++ b/packages/adapter-pglite/schema.sql @@ -0,0 +1,140 @@ +-- Enable pgvector extension + +-- -- Drop existing tables and extensions +-- DROP EXTENSION IF EXISTS vector CASCADE; +-- DROP TABLE IF EXISTS relationships CASCADE; +-- DROP TABLE IF EXISTS participants CASCADE; +-- DROP TABLE IF EXISTS logs CASCADE; +-- DROP TABLE IF EXISTS goals CASCADE; +-- DROP TABLE IF EXISTS memories CASCADE; +-- DROP TABLE IF EXISTS rooms CASCADE; +-- DROP TABLE IF EXISTS accounts CASCADE; + + +CREATE EXTENSION IF NOT EXISTS vector; +CREATE EXTENSION IF NOT EXISTS fuzzystrmatch; + +-- Create a function to determine vector dimension +CREATE OR REPLACE FUNCTION get_embedding_dimension() +RETURNS INTEGER AS $$ +BEGIN + -- Check for OpenAI first + IF current_setting('app.use_openai_embedding', TRUE) = 'true' THEN + RETURN 1536; -- OpenAI dimension + -- Then check for Ollama + ELSIF current_setting('app.use_ollama_embedding', TRUE) = 'true' THEN + RETURN 1024; -- Ollama mxbai-embed-large dimension + -- Then check for GAIANET + ELSIF current_setting('app.use_gaianet_embedding', TRUE) = 'true' THEN + RETURN 768; -- Gaianet nomic-embed dimension + ELSE + RETURN 384; -- BGE/Other embedding dimension + END IF; +END; +$$ LANGUAGE plpgsql; + +BEGIN; + +CREATE TABLE IF NOT EXISTS accounts ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "name" TEXT, + "username" TEXT, + "email" TEXT NOT NULL, + "avatarUrl" TEXT, + "details" JSONB DEFAULT '{}'::jsonb +); + +CREATE TABLE IF NOT EXISTS rooms ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP +); + +DO $$ +DECLARE + vector_dim INTEGER; +BEGIN + vector_dim := get_embedding_dimension(); + + EXECUTE format(' + CREATE TABLE IF NOT EXISTS memories ( + "id" UUID PRIMARY KEY, + "type" TEXT NOT NULL, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "content" JSONB NOT NULL, + "embedding" vector(%s), + "userId" UUID REFERENCES accounts("id"), + "agentId" UUID REFERENCES accounts("id"), + "roomId" UUID REFERENCES rooms("id"), + "unique" BOOLEAN DEFAULT true NOT NULL, + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE, + CONSTRAINT fk_agent FOREIGN KEY ("agentId") REFERENCES accounts("id") ON DELETE CASCADE + )', vector_dim); +END $$; + +CREATE TABLE IF NOT EXISTS goals ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID REFERENCES accounts("id"), + "name" TEXT, + "status" TEXT, + "description" TEXT, + "roomId" UUID REFERENCES rooms("id"), + "objectives" JSONB DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS logs ( + "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID NOT NULL REFERENCES accounts("id"), + "body" JSONB NOT NULL, + "type" TEXT NOT NULL, + "roomId" UUID NOT NULL REFERENCES rooms("id"), + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS participants ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userId" UUID REFERENCES accounts("id"), + "roomId" UUID REFERENCES rooms("id"), + "userState" TEXT, + "last_message_read" TEXT, + UNIQUE("userId", "roomId"), + CONSTRAINT fk_room FOREIGN KEY ("roomId") REFERENCES rooms("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS relationships ( + "id" UUID PRIMARY KEY, + "createdAt" TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + "userA" UUID NOT NULL REFERENCES accounts("id"), + "userB" UUID NOT NULL REFERENCES accounts("id"), + "status" TEXT, + "userId" UUID NOT NULL REFERENCES accounts("id"), + CONSTRAINT fk_user_a FOREIGN KEY ("userA") REFERENCES accounts("id") ON DELETE CASCADE, + CONSTRAINT fk_user_b FOREIGN KEY ("userB") REFERENCES accounts("id") ON DELETE CASCADE, + CONSTRAINT fk_user FOREIGN KEY ("userId") REFERENCES accounts("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS cache ( + "key" TEXT NOT NULL, + "agentId" TEXT NOT NULL, + "value" JSONB DEFAULT '{}'::jsonb, + "createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "expiresAt" TIMESTAMP, + PRIMARY KEY ("key", "agentId") +); + +-- Indexes +CREATE INDEX IF NOT EXISTS idx_memories_embedding ON memories USING hnsw ("embedding" vector_cosine_ops); +CREATE INDEX IF NOT EXISTS idx_memories_type_room ON memories("type", "roomId"); +CREATE INDEX IF NOT EXISTS idx_participants_user ON participants("userId"); +CREATE INDEX IF NOT EXISTS idx_participants_room ON participants("roomId"); +CREATE INDEX IF NOT EXISTS idx_relationships_users ON relationships("userA", "userB"); + +COMMIT; diff --git a/packages/adapter-pglite/src/index.ts b/packages/adapter-pglite/src/index.ts new file mode 100644 index 0000000000..cd0782032a --- /dev/null +++ b/packages/adapter-pglite/src/index.ts @@ -0,0 +1,1355 @@ +import { v4 } from "uuid"; + +// Import the entire module as default +import pg from "pg"; +type Pool = pg.Pool; + +import { + QueryConfig, + QueryConfigValues, + QueryResult, + QueryResultRow, +} from "pg"; +import { + Account, + Actor, + GoalStatus, + type Goal, + type Memory, + type Relationship, + type UUID, + type IDatabaseCacheAdapter, + Participant, + elizaLogger, + getEmbeddingConfig, + DatabaseAdapter, + EmbeddingProvider, +} from "@elizaos/core"; +import fs from "fs"; +import { fileURLToPath } from "url"; +import path from "path"; +import { PGlite, Results } from "@electric-sql/pglite"; + +const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file +const __dirname = path.dirname(__filename); // get the name of the directory + +export class PGLiteDatabaseAdapter + extends DatabaseAdapter + implements IDatabaseCacheAdapter +{ + private readonly maxRetries: number = 3; + private readonly baseDelay: number = 1000; // 1 second + private readonly maxDelay: number = 10000; // 10 seconds + private readonly jitterMax: number = 1000; // 1 second + private readonly connectionTimeout: number = 5000; // 5 seconds + + constructor(db: PGlite) { + super(); + this.db = db; + } + + async init() { + await this.db.waitReady; + + this.db.transaction(async (tx) => { + // Set application settings for embedding dimension + const embeddingConfig = getEmbeddingConfig(); + if (embeddingConfig.provider === EmbeddingProvider.OpenAI) { + await tx.query("SET app.use_openai_embedding = 'true'"); + await tx.query("SET app.use_ollama_embedding = 'false'"); + await tx.query("SET app.use_gaianet_embedding = 'false'"); + } else if (embeddingConfig.provider === EmbeddingProvider.Ollama) { + await tx.query("SET app.use_openai_embedding = 'false'"); + await tx.query("SET app.use_ollama_embedding = 'true'"); + await tx.query("SET app.use_gaianet_embedding = 'false'"); + } else if (embeddingConfig.provider === EmbeddingProvider.GaiaNet) { + await tx.query("SET app.use_openai_embedding = 'false'"); + await tx.query("SET app.use_ollama_embedding = 'false'"); + await tx.query("SET app.use_gaianet_embedding = 'true'"); + } else { + await tx.query("SET app.use_openai_embedding = 'false'"); + await tx.query("SET app.use_ollama_embedding = 'false'"); + await tx.query("SET app.use_gaianet_embedding = 'false'"); + } + + const schema = fs.readFileSync( + path.resolve(__dirname, "../schema.sql"), + "utf8" + ); + await tx.query(schema); + }); + } + + async close() { + await this.db.close(); + } + + private async withDatabase( + operation: () => Promise, + context: string + ): Promise { + return this.withCircuitBreaker(async () => { + return this.withRetry(operation); + }, context); + } + + private async withRetry(operation: () => Promise): Promise { + let lastError: Error = new Error("Unknown error"); // Initialize with default + + for (let attempt = 1; attempt <= this.maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error as Error; + + if (attempt < this.maxRetries) { + // Calculate delay with exponential backoff + const backoffDelay = Math.min( + this.baseDelay * Math.pow(2, attempt - 1), + this.maxDelay + ); + + // Add jitter to prevent thundering herd + const jitter = Math.random() * this.jitterMax; + const delay = backoffDelay + jitter; + + elizaLogger.warn( + `Database operation failed (attempt ${attempt}/${this.maxRetries}):`, + { + error: + error instanceof Error + ? error.message + : String(error), + nextRetryIn: `${(delay / 1000).toFixed(1)}s`, + } + ); + + await new Promise((resolve) => setTimeout(resolve, delay)); + } else { + elizaLogger.error("Max retry attempts reached:", { + error: + error instanceof Error + ? error.message + : String(error), + totalAttempts: attempt, + }); + throw error instanceof Error + ? error + : new Error(String(error)); + } + } + } + + throw lastError; + } + + async query( + queryTextOrConfig: string, + values?: QueryConfigValues + ): Promise> { + return this.withDatabase(async () => { + return await this.db.query(queryTextOrConfig, values); + }, "query"); + } + + async getRoom(roomId: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query<{ id: UUID }>( + "SELECT id FROM rooms WHERE id = $1", + [roomId] + ); + return rows.length > 0 ? rows[0].id : null; + }, "getRoom"); + } + + async getParticipantsForAccount(userId: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query( + `SELECT id, "userId", "roomId", "last_message_read" + FROM participants + WHERE "userId" = $1`, + [userId] + ); + return rows; + }, "getParticipantsForAccount"); + } + + async getParticipantUserState( + roomId: UUID, + userId: UUID + ): Promise<"FOLLOWED" | "MUTED" | null> { + return this.withDatabase(async () => { + const { rows } = await this.db.query<{ + userState: "FOLLOWED" | "MUTED"; + }>( + `SELECT "userState" FROM participants WHERE "roomId" = $1 AND "userId" = $2`, + [roomId, userId] + ); + return rows.length > 0 ? rows[0].userState : null; + }, "getParticipantUserState"); + } + + async getMemoriesByRoomIds(params: { + roomIds: UUID[]; + agentId?: UUID; + tableName: string; + }): Promise { + return this.withDatabase(async () => { + if (params.roomIds.length === 0) return []; + const placeholders = params.roomIds + .map((_, i) => `$${i + 2}`) + .join(", "); + + let query = `SELECT * FROM memories WHERE type = $1 AND "roomId" IN (${placeholders})`; + let queryParams = [params.tableName, ...params.roomIds]; + + if (params.agentId) { + query += ` AND "agentId" = $${params.roomIds.length + 2}`; + queryParams = [...queryParams, params.agentId]; + } + + const { rows } = await this.db.query(query, queryParams); + return rows.map((row) => ({ + ...row, + content: + typeof row.content === "string" + ? JSON.parse(row.content) + : row.content, + })); + }, "getMemoriesByRoomIds"); + } + + async setParticipantUserState( + roomId: UUID, + userId: UUID, + state: "FOLLOWED" | "MUTED" | null + ): Promise { + return this.withDatabase(async () => { + await this.db.query( + `UPDATE participants SET "userState" = $1 WHERE "roomId" = $2 AND "userId" = $3`, + [state, roomId, userId] + ); + }, "setParticipantUserState"); + } + + async getParticipantsForRoom(roomId: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query<{ userId: UUID }>( + 'SELECT "userId" FROM participants WHERE "roomId" = $1', + [roomId] + ); + return rows.map((row) => row.userId); + }, "getParticipantsForRoom"); + } + + async getAccountById(userId: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query( + "SELECT * FROM accounts WHERE id = $1", + [userId] + ); + if (rows.length === 0) { + elizaLogger.debug("Account not found:", { userId }); + return null; + } + + const account = rows[0]; + // elizaLogger.debug("Account retrieved:", { + // userId, + // hasDetails: !!account.details, + // }); + + return { + ...account, + details: + typeof account.details === "string" + ? JSON.parse(account.details) + : account.details, + }; + }, "getAccountById"); + } + + async createAccount(account: Account): Promise { + return this.withDatabase(async () => { + try { + const accountId = account.id ?? v4(); + await this.db.query( + `INSERT INTO accounts (id, name, username, email, "avatarUrl", details) + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + accountId, + account.name, + account.username || "", + account.email || "", + account.avatarUrl || "", + JSON.stringify(account.details), + ] + ); + elizaLogger.debug("Account created successfully:", { + accountId, + }); + return true; + } catch (error) { + elizaLogger.error("Error creating account:", { + error: + error instanceof Error ? error.message : String(error), + accountId: account.id, + name: account.name, // Only log non-sensitive fields + }); + return false; // Return false instead of throwing to maintain existing behavior + } + }, "createAccount"); + } + + async getActorById(params: { roomId: UUID }): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query( + `SELECT a.id, a.name, a.username, a.details + FROM participants p + LEFT JOIN accounts a ON p."userId" = a.id + WHERE p."roomId" = $1`, + [params.roomId] + ); + + elizaLogger.debug("Retrieved actors:", { + roomId: params.roomId, + actorCount: rows.length, + }); + + return rows.map((row) => { + try { + return { + ...row, + details: + typeof row.details === "string" + ? JSON.parse(row.details) + : row.details, + }; + } catch (error) { + elizaLogger.warn("Failed to parse actor details:", { + actorId: row.id, + error: + error instanceof Error + ? error.message + : String(error), + }); + return { + ...row, + details: {}, // Provide default empty details on parse error + }; + } + }); + }, "getActorById").catch((error) => { + elizaLogger.error("Failed to get actors:", { + roomId: params.roomId, + error: error.message, + }); + throw error; // Re-throw to let caller handle database errors + }); + } + + async getMemoryById(id: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query( + "SELECT * FROM memories WHERE id = $1", + [id] + ); + if (rows.length === 0) return null; + + return { + ...rows[0], + content: + typeof rows[0].content === "string" + ? JSON.parse(rows[0].content) + : rows[0].content, + }; + }, "getMemoryById"); + } + + async createMemory(memory: Memory, tableName: string): Promise { + return this.withDatabase(async () => { + elizaLogger.debug("PostgresAdapter createMemory:", { + memoryId: memory.id, + embeddingLength: memory.embedding?.length, + contentLength: memory.content?.text?.length, + }); + + let isUnique = true; + if (memory.embedding) { + const similarMemories = await this.searchMemoriesByEmbedding( + memory.embedding, + { + tableName, + roomId: memory.roomId, + match_threshold: 0.95, + count: 1, + } + ); + isUnique = similarMemories.length === 0; + } + + await this.db.query( + `INSERT INTO memories ( + id, type, content, embedding, "userId", "roomId", "agentId", "unique", "createdAt" + ) VALUES ($1, $2, $3, $4, $5::uuid, $6::uuid, $7::uuid, $8, to_timestamp($9/1000.0))`, + [ + memory.id ?? v4(), + tableName, + JSON.stringify(memory.content), + memory.embedding ? `[${memory.embedding.join(",")}]` : null, + memory.userId, + memory.roomId, + memory.agentId, + memory.unique ?? isUnique, + Date.now(), + ] + ); + }, "createMemory"); + } + + async searchMemories(params: { + tableName: string; + agentId: UUID; + roomId: UUID; + embedding: number[]; + match_threshold: number; + match_count: number; + unique: boolean; + }): Promise { + return await this.searchMemoriesByEmbedding(params.embedding, { + match_threshold: params.match_threshold, + count: params.match_count, + agentId: params.agentId, + roomId: params.roomId, + unique: params.unique, + tableName: params.tableName, + }); + } + + async getMemories(params: { + roomId: UUID; + count?: number; + unique?: boolean; + tableName: string; + agentId?: UUID; + start?: number; + end?: number; + }): Promise { + // Parameter validation + if (!params.tableName) throw new Error("tableName is required"); + if (!params.roomId) throw new Error("roomId is required"); + + return this.withDatabase(async () => { + // Build query + let sql = `SELECT * FROM memories WHERE type = $1 AND "roomId" = $2`; + const values: any[] = [params.tableName, params.roomId]; + let paramCount = 2; + + // Add time range filters + if (params.start) { + paramCount++; + sql += ` AND "createdAt" >= to_timestamp($${paramCount})`; + values.push(params.start / 1000); + } + + if (params.end) { + paramCount++; + sql += ` AND "createdAt" <= to_timestamp($${paramCount})`; + values.push(params.end / 1000); + } + + // Add other filters + if (params.unique) { + sql += ` AND "unique" = true`; + } + + if (params.agentId) { + paramCount++; + sql += ` AND "agentId" = $${paramCount}`; + values.push(params.agentId); + } + + // Add ordering and limit + sql += ' ORDER BY "createdAt" DESC'; + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + elizaLogger.debug("Fetching memories:", { + roomId: params.roomId, + tableName: params.tableName, + unique: params.unique, + agentId: params.agentId, + timeRange: + params.start || params.end + ? { + start: params.start + ? new Date(params.start).toISOString() + : undefined, + end: params.end + ? new Date(params.end).toISOString() + : undefined, + } + : undefined, + limit: params.count, + }); + + const { rows } = await this.db.query(sql, values); + return rows.map((row) => ({ + ...row, + content: + typeof row.content === "string" + ? JSON.parse(row.content) + : row.content, + })); + }, "getMemories"); + } + + async getGoals(params: { + roomId: UUID; + userId?: UUID | null; + onlyInProgress?: boolean; + count?: number; + }): Promise { + return this.withDatabase(async () => { + let sql = `SELECT * FROM goals WHERE "roomId" = $1`; + const values: any[] = [params.roomId]; + let paramCount = 1; + + if (params.userId) { + paramCount++; + sql += ` AND "userId" = $${paramCount}`; + values.push(params.userId); + } + + if (params.onlyInProgress) { + sql += " AND status = 'IN_PROGRESS'"; + } + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + const { rows } = await this.db.query(sql, values); + return rows.map((row) => ({ + ...row, + objectives: + typeof row.objectives === "string" + ? JSON.parse(row.objectives) + : row.objectives, + })); + }, "getGoals"); + } + + async updateGoal(goal: Goal): Promise { + return this.withDatabase(async () => { + try { + await this.db.query( + `UPDATE goals SET name = $1, status = $2, objectives = $3 WHERE id = $4`, + [ + goal.name, + goal.status, + JSON.stringify(goal.objectives), + goal.id, + ] + ); + } catch (error) { + elizaLogger.error("Failed to update goal:", { + goalId: goal.id, + error: + error instanceof Error ? error.message : String(error), + status: goal.status, + }); + throw error; + } + }, "updateGoal"); + } + + async createGoal(goal: Goal): Promise { + return this.withDatabase(async () => { + await this.db.query( + `INSERT INTO goals (id, "roomId", "userId", name, status, objectives) + VALUES ($1, $2, $3, $4, $5, $6)`, + [ + goal.id ?? v4(), + goal.roomId, + goal.userId, + goal.name, + goal.status, + JSON.stringify(goal.objectives), + ] + ); + }, "createGoal"); + } + + async removeGoal(goalId: UUID): Promise { + if (!goalId) throw new Error("Goal ID is required"); + + return this.withDatabase(async () => { + try { + const result = await this.db.query( + "DELETE FROM goals WHERE id = $1 RETURNING id", + [goalId] + ); + + elizaLogger.debug("Goal removal attempt:", { + goalId, + removed: result?.affectedRows ?? 0 > 0, + }); + } catch (error) { + elizaLogger.error("Failed to remove goal:", { + goalId, + error: + error instanceof Error ? error.message : String(error), + }); + throw error; + } + }, "removeGoal"); + } + + async createRoom(roomId?: UUID): Promise { + return this.withDatabase(async () => { + const newRoomId = roomId || v4(); + await this.db.query("INSERT INTO rooms (id) VALUES ($1)", [ + newRoomId, + ]); + return newRoomId as UUID; + }, "createRoom"); + } + + async removeRoom(roomId: UUID): Promise { + if (!roomId) throw new Error("Room ID is required"); + + return this.withDatabase(async () => { + await this.db.transaction(async (tx) => { + try { + // First check if room exists + const checkResult = await tx.query( + "SELECT id FROM rooms WHERE id = $1", + [roomId] + ); + + if (checkResult.rows.length === 0) { + elizaLogger.warn("No room found to remove:", { + roomId, + }); + throw new Error(`Room not found: ${roomId}`); + } + + // Remove related data first (if not using CASCADE) + await tx.query('DELETE FROM memories WHERE "roomId" = $1', [ + roomId, + ]); + await tx.query( + 'DELETE FROM participants WHERE "roomId" = $1', + [roomId] + ); + await tx.query('DELETE FROM goals WHERE "roomId" = $1', [ + roomId, + ]); + + // Finally remove the room + const result = await tx.query( + "DELETE FROM rooms WHERE id = $1 RETURNING id", + [roomId] + ); + + elizaLogger.debug( + "Room and related data removed successfully:", + { + roomId, + removed: result?.affectedRows ?? 0 > 0, + } + ); + } catch (error) { + elizaLogger.error("Failed to remove room:", { + roomId, + error: + error instanceof Error + ? error.message + : String(error), + }); + throw error; + } + }); + }, "removeRoom"); + } + + async createRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + // Input validation + if (!params.userA || !params.userB) { + throw new Error("userA and userB are required"); + } + + return this.withDatabase(async () => { + try { + const relationshipId = v4(); + await this.db.query( + `INSERT INTO relationships (id, "userA", "userB", "userId") + VALUES ($1, $2, $3, $4) + RETURNING id`, + [relationshipId, params.userA, params.userB, params.userA] + ); + + elizaLogger.debug("Relationship created successfully:", { + relationshipId, + userA: params.userA, + userB: params.userB, + }); + + return true; + } catch (error) { + // Check for unique constraint violation or other specific errors + if ((error as { code?: string }).code === "23505") { + // Unique violation + elizaLogger.warn("Relationship already exists:", { + userA: params.userA, + userB: params.userB, + error: + error instanceof Error + ? error.message + : String(error), + }); + } else { + elizaLogger.error("Failed to create relationship:", { + userA: params.userA, + userB: params.userB, + error: + error instanceof Error + ? error.message + : String(error), + }); + } + return false; + } + }, "createRelationship"); + } + + async getRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + if (!params.userA || !params.userB) { + throw new Error("userA and userB are required"); + } + + return this.withDatabase(async () => { + try { + const { rows } = await this.db.query( + `SELECT * FROM relationships + WHERE ("userA" = $1 AND "userB" = $2) + OR ("userA" = $2 AND "userB" = $1)`, + [params.userA, params.userB] + ); + + if (rows.length > 0) { + elizaLogger.debug("Relationship found:", { + relationshipId: rows[0].id, + userA: params.userA, + userB: params.userB, + }); + return rows[0]; + } + + elizaLogger.debug("No relationship found between users:", { + userA: params.userA, + userB: params.userB, + }); + return null; + } catch (error) { + elizaLogger.error("Error fetching relationship:", { + userA: params.userA, + userB: params.userB, + error: + error instanceof Error ? error.message : String(error), + }); + throw error; + } + }, "getRelationship"); + } + + async getRelationships(params: { userId: UUID }): Promise { + if (!params.userId) { + throw new Error("userId is required"); + } + + return this.withDatabase(async () => { + try { + const { rows } = await this.db.query( + `SELECT * FROM relationships + WHERE "userA" = $1 OR "userB" = $1 + ORDER BY "createdAt" DESC`, // Add ordering if you have this field + [params.userId] + ); + + elizaLogger.debug("Retrieved relationships:", { + userId: params.userId, + count: rows.length, + }); + + return rows; + } catch (error) { + elizaLogger.error("Failed to fetch relationships:", { + userId: params.userId, + error: + error instanceof Error ? error.message : String(error), + }); + throw error; + } + }, "getRelationships"); + } + + async getCachedEmbeddings(opts: { + query_table_name: string; + query_threshold: number; + query_input: string; + query_field_name: string; + query_field_sub_name: string; + query_match_count: number; + }): Promise<{ embedding: number[]; levenshtein_score: number }[]> { + // Input validation + if (!opts.query_table_name) + throw new Error("query_table_name is required"); + if (!opts.query_input) throw new Error("query_input is required"); + if (!opts.query_field_name) + throw new Error("query_field_name is required"); + if (!opts.query_field_sub_name) + throw new Error("query_field_sub_name is required"); + if (opts.query_match_count <= 0) + throw new Error("query_match_count must be positive"); + + return this.withDatabase(async () => { + try { + elizaLogger.debug("Fetching cached embeddings:", { + tableName: opts.query_table_name, + fieldName: opts.query_field_name, + subFieldName: opts.query_field_sub_name, + matchCount: opts.query_match_count, + inputLength: opts.query_input.length, + }); + + const sql = ` + WITH content_text AS ( + SELECT + embedding, + COALESCE( + content->$2->>$3, + '' + ) as content_text + FROM memories + WHERE type = $4 + AND content->$2->>$3 IS NOT NULL + ) + SELECT + embedding, + levenshtein( + $1, + content_text + ) as levenshtein_score + FROM content_text + WHERE levenshtein( + $1, + content_text + ) <= $6 -- Add threshold check + ORDER BY levenshtein_score + LIMIT $5 + `; + + const { rows } = await this.db.query<{ embedding: number[], levenshtein_score: number }>(sql, [ + opts.query_input, + opts.query_field_name, + opts.query_field_sub_name, + opts.query_table_name, + opts.query_match_count, + opts.query_threshold, + ]); + + elizaLogger.debug("Retrieved cached embeddings:", { + count: rows.length, + tableName: opts.query_table_name, + matchCount: opts.query_match_count, + }); + + return rows + .map( + ( + row + ): { + embedding: number[]; + levenshtein_score: number; + } | null => { + if (!Array.isArray(row.embedding)) return null; + return { + embedding: row.embedding, + levenshtein_score: Number( + row.levenshtein_score + ), + }; + } + ) + .filter( + ( + row + ): row is { + embedding: number[]; + levenshtein_score: number; + } => row !== null + ); + } catch (error) { + elizaLogger.error("Error in getCachedEmbeddings:", { + error: + error instanceof Error ? error.message : String(error), + tableName: opts.query_table_name, + fieldName: opts.query_field_name, + }); + throw error; + } + }, "getCachedEmbeddings"); + } + + async log(params: { + body: { [key: string]: unknown }; + userId: UUID; + roomId: UUID; + type: string; + }): Promise { + // Input validation + if (!params.userId) throw new Error("userId is required"); + if (!params.roomId) throw new Error("roomId is required"); + if (!params.type) throw new Error("type is required"); + if (!params.body || typeof params.body !== "object") { + throw new Error("body must be a valid object"); + } + + return this.withDatabase(async () => { + try { + const logId = v4(); // Generate ID for tracking + await this.db.query( + `INSERT INTO logs ( + id, + body, + "userId", + "roomId", + type, + "createdAt" + ) VALUES ($1, $2, $3, $4, $5, NOW()) + RETURNING id`, + [ + logId, + JSON.stringify(params.body), // Ensure body is stringified + params.userId, + params.roomId, + params.type, + ] + ); + + elizaLogger.debug("Log entry created:", { + logId, + type: params.type, + roomId: params.roomId, + userId: params.userId, + bodyKeys: Object.keys(params.body), + }); + } catch (error) { + elizaLogger.error("Failed to create log entry:", { + error: + error instanceof Error ? error.message : String(error), + type: params.type, + roomId: params.roomId, + userId: params.userId, + }); + throw error; + } + }, "log"); + } + + async searchMemoriesByEmbedding( + embedding: number[], + params: { + match_threshold?: number; + count?: number; + agentId?: UUID; + roomId?: UUID; + unique?: boolean; + tableName: string; + } + ): Promise { + return this.withDatabase(async () => { + elizaLogger.debug("Incoming vector:", { + length: embedding.length, + sample: embedding.slice(0, 5), + isArray: Array.isArray(embedding), + allNumbers: embedding.every((n) => typeof n === "number"), + }); + + // Validate embedding dimension + if (embedding.length !== getEmbeddingConfig().dimensions) { + throw new Error( + `Invalid embedding dimension: expected ${getEmbeddingConfig().dimensions}, got ${embedding.length}` + ); + } + + // Ensure vector is properly formatted + const cleanVector = embedding.map((n) => { + if (!Number.isFinite(n)) return 0; + // Limit precision to avoid floating point issues + return Number(n.toFixed(6)); + }); + + // Format for Postgres pgvector + const vectorStr = `[${cleanVector.join(",")}]`; + + elizaLogger.debug("Vector debug:", { + originalLength: embedding.length, + cleanLength: cleanVector.length, + sampleStr: vectorStr.slice(0, 100), + }); + + let sql = ` + SELECT *, + 1 - (embedding <-> $1::vector(${getEmbeddingConfig().dimensions})) as similarity + FROM memories + WHERE type = $2 + `; + + const values: any[] = [vectorStr, params.tableName]; + + // Log the query for debugging + elizaLogger.debug("Query debug:", { + sql: sql.slice(0, 200), + paramTypes: values.map((v) => typeof v), + vectorStrLength: vectorStr.length, + }); + + let paramCount = 2; + + if (params.unique) { + sql += ` AND "unique" = true`; + } + + if (params.agentId) { + paramCount++; + sql += ` AND "agentId" = $${paramCount}`; + values.push(params.agentId); + } + + if (params.roomId) { + paramCount++; + sql += ` AND "roomId" = $${paramCount}::uuid`; + values.push(params.roomId); + } + + if (params.match_threshold) { + paramCount++; + sql += ` AND 1 - (embedding <-> $1::vector) >= $${paramCount}`; + values.push(params.match_threshold); + } + + sql += ` ORDER BY embedding <-> $1::vector`; + + if (params.count) { + paramCount++; + sql += ` LIMIT $${paramCount}`; + values.push(params.count); + } + + const { rows } = await this.db.query(sql, values); + return rows.map((row) => ({ + ...row, + content: + typeof row.content === "string" + ? JSON.parse(row.content) + : row.content, + similarity: row.similarity, + })); + }, "searchMemoriesByEmbedding"); + } + + async addParticipant(userId: UUID, roomId: UUID): Promise { + return this.withDatabase(async () => { + try { + await this.db.query( + `INSERT INTO participants (id, "userId", "roomId") + VALUES ($1, $2, $3)`, + [v4(), userId, roomId] + ); + return true; + } catch (error) { + console.log("Error adding participant", error); + return false; + } + }, "addParticpant"); + } + + async removeParticipant(userId: UUID, roomId: UUID): Promise { + return this.withDatabase(async () => { + try { + await this.db.query( + `DELETE FROM participants WHERE "userId" = $1 AND "roomId" = $2`, + [userId, roomId] + ); + return true; + } catch (error) { + console.log("Error removing participant", error); + return false; + } + }, "removeParticipant"); + } + + async updateGoalStatus(params: { + goalId: UUID; + status: GoalStatus; + }): Promise { + return this.withDatabase(async () => { + await this.db.query("UPDATE goals SET status = $1 WHERE id = $2", [ + params.status, + params.goalId, + ]); + }, "updateGoalStatus"); + } + + async removeMemory(memoryId: UUID, tableName: string): Promise { + return this.withDatabase(async () => { + await this.db.query( + "DELETE FROM memories WHERE type = $1 AND id = $2", + [tableName, memoryId] + ); + }, "removeMemory"); + } + + async removeAllMemories(roomId: UUID, tableName: string): Promise { + return this.withDatabase(async () => { + await this.db.query( + `DELETE FROM memories WHERE type = $1 AND "roomId" = $2`, + [tableName, roomId] + ); + }, "removeAllMemories"); + } + + async countMemories( + roomId: UUID, + unique = true, + tableName = "" + ): Promise { + if (!tableName) throw new Error("tableName is required"); + + return this.withDatabase(async () => { + let sql = `SELECT COUNT(*) as count FROM memories WHERE type = $1 AND "roomId" = $2`; + if (unique) { + sql += ` AND "unique" = true`; + } + + const { rows } = await this.db.query<{ count: number }>(sql, [ + tableName, + roomId, + ]); + return rows[0].count; + }, "countMemories"); + } + + async removeAllGoals(roomId: UUID): Promise { + return this.withDatabase(async () => { + await this.db.query(`DELETE FROM goals WHERE "roomId" = $1`, [ + roomId, + ]); + }, "removeAllGoals"); + } + + async getRoomsForParticipant(userId: UUID): Promise { + return this.withDatabase(async () => { + const { rows } = await this.db.query<{ roomId: UUID }>( + `SELECT "roomId" FROM participants WHERE "userId" = $1`, + [userId] + ); + return rows.map((row) => row.roomId); + }, "getRoomsForParticipant"); + } + + async getRoomsForParticipants(userIds: UUID[]): Promise { + return this.withDatabase(async () => { + const placeholders = userIds.map((_, i) => `$${i + 1}`).join(", "); + const { rows } = await this.db.query<{ roomId: UUID }>( + `SELECT DISTINCT "roomId" FROM participants WHERE "userId" IN (${placeholders})`, + userIds + ); + return rows.map((row) => row.roomId); + }, "getRoomsForParticipants"); + } + + async getActorDetails(params: { roomId: string }): Promise { + if (!params.roomId) { + throw new Error("roomId is required"); + } + + return this.withDatabase(async () => { + try { + const sql = ` + SELECT + a.id, + a.name, + a.username, + a."avatarUrl", + COALESCE(a.details::jsonb, '{}'::jsonb) as details + FROM participants p + LEFT JOIN accounts a ON p."userId" = a.id + WHERE p."roomId" = $1 + ORDER BY a.name + `; + + const result = await this.db.query(sql, [params.roomId]); + + elizaLogger.debug("Retrieved actor details:", { + roomId: params.roomId, + actorCount: result.rows.length, + }); + + return result.rows.map((row) => { + try { + return { + ...row, + details: + typeof row.details === "string" + ? JSON.parse(row.details) + : row.details, + }; + } catch (parseError) { + elizaLogger.warn("Failed to parse actor details:", { + actorId: row.id, + error: + parseError instanceof Error + ? parseError.message + : String(parseError), + }); + return { + ...row, + details: {}, // Fallback to empty object if parsing fails + }; + } + }); + } catch (error) { + elizaLogger.error("Failed to fetch actor details:", { + roomId: params.roomId, + error: + error instanceof Error ? error.message : String(error), + }); + throw new Error( + `Failed to fetch actor details: ${error instanceof Error ? error.message : String(error)}` + ); + } + }, "getActorDetails"); + } + + async getCache(params: { + key: string; + agentId: UUID; + }): Promise { + return this.withDatabase(async () => { + try { + const sql = `SELECT "value"::TEXT FROM cache WHERE "key" = $1 AND "agentId" = $2`; + const { rows } = await this.query<{ value: string }>(sql, [ + params.key, + params.agentId, + ]); + return rows[0]?.value ?? undefined; + } catch (error) { + elizaLogger.error("Error fetching cache", { + error: + error instanceof Error ? error.message : String(error), + key: params.key, + agentId: params.agentId, + }); + return undefined; + } + }, "getCache"); + } + + async setCache(params: { + key: string; + agentId: UUID; + value: string; + }): Promise { + return this.withDatabase(async () => { + return ( + (await this.db.transaction(async (tx) => { + try { + try { + await tx.query( + `INSERT INTO cache ("key", "agentId", "value", "createdAt") + VALUES ($1, $2, $3, CURRENT_TIMESTAMP) + ON CONFLICT ("key", "agentId") + DO UPDATE SET "value" = EXCLUDED.value, "createdAt" = CURRENT_TIMESTAMP`, + [params.key, params.agentId, params.value] + ); + return true; + } catch (error) { + await tx.rollback(); + elizaLogger.error("Error setting cache", { + error: + error instanceof Error + ? error.message + : String(error), + key: params.key, + agentId: params.agentId, + }); + return false; + } + } catch (error) { + elizaLogger.error( + "Database connection error in setCache", + error + ); + return false; + } + })) ?? false + ); + }, "setCache"); + } + + async deleteCache(params: { + key: string; + agentId: UUID; + }): Promise { + return this.withDatabase(async () => { + return ( + (await this.db.transaction(async (tx) => { + try { + try { + await tx.query( + `DELETE FROM cache WHERE "key" = $1 AND "agentId" = $2`, + [params.key, params.agentId] + ); + return true; + } catch (error) { + tx.rollback(); + elizaLogger.error("Error deleting cache", { + error: + error instanceof Error + ? error.message + : String(error), + key: params.key, + agentId: params.agentId, + }); + return false; + } + } catch (error) { + elizaLogger.error( + "Database connection error in deleteCache", + error + ); + return false; + } + })) ?? false + ); + }, "deleteCache"); + } +} + +export default PGLiteDatabaseAdapter; diff --git a/packages/adapter-pglite/tsconfig.json b/packages/adapter-pglite/tsconfig.json new file mode 100644 index 0000000000..673cf100f4 --- /dev/null +++ b/packages/adapter-pglite/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "strict": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/adapter-pglite/tsup.config.ts b/packages/adapter-pglite/tsup.config.ts new file mode 100644 index 0000000000..964bdc8685 --- /dev/null +++ b/packages/adapter-pglite/tsup.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "@anush008/tokenizers", + "uuid", + // Add other modules you want to externalize + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90f147c715..fc9fac569d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -412,6 +412,22 @@ importers: specifier: 4.2.10 version: 4.2.10(typedoc@0.26.11(typescript@5.6.3)) + packages/adapter-pglite: + dependencies: + '@electric-sql/pglite': + specifier: ^0.2.15 + version: 0.2.15 + '@elizaos/core': + specifier: workspace:* + version: link:../core + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + devDependencies: + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/adapter-postgres: dependencies: '@elizaos/core': @@ -3871,6 +3887,9 @@ packages: peerDependencies: onnxruntime-node: 1.20.1 + '@electric-sql/pglite@0.2.15': + resolution: {integrity: sha512-Jiq31Dnk+rg8rMhcSxs4lQvHTyizNo5b269c1gCC3ldQ0sCLrNVPGzy+KnmonKy1ZArTUuXZf23/UamzFMKVaA==} + '@elizaos/core@0.1.7-alpha.2': resolution: {integrity: sha512-gNvFw/Xnv4dlcfmmKxRa+baKq6en4TitAjUGvo8LgAUkSk156A0fffJ0lAsc1rX8zMB5NsIqdvMCbwKxDd54OQ==} @@ -23022,6 +23041,8 @@ snapshots: '@huggingface/jinja': 0.2.2 onnxruntime-node: 1.20.1 + '@electric-sql/pglite@0.2.15': {} + '@elizaos/core@0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)': dependencies: '@ai-sdk/anthropic': 0.0.56(zod@3.23.8) From 4cb867df4e120e5055be850fe44776ecfc550ca9 Mon Sep 17 00:00:00 2001 From: KONFeature Date: Sat, 4 Jan 2025 11:48:44 +0100 Subject: [PATCH 21/58] feat: cleanup code and wrap it up --- agent/package.json | 3 +- agent/src/index.ts | 7 + packages/adapter-pglite/src/index.ts | 376 +++++++----------- .../core/src/test_resources/createRuntime.ts | 18 + pnpm-lock.yaml | 87 +--- 5 files changed, 195 insertions(+), 296 deletions(-) diff --git a/agent/package.json b/agent/package.json index f4fa0f33e0..e73c373f4d 100644 --- a/agent/package.json +++ b/agent/package.json @@ -21,6 +21,7 @@ "@elizaos/adapter-postgres": "workspace:*", "@elizaos/adapter-redis": "workspace:*", "@elizaos/adapter-sqlite": "workspace:*", + "@elizaos/adapter-pglite": "workspace:*", "@elizaos/client-auto": "workspace:*", "@elizaos/client-direct": "workspace:*", "@elizaos/client-discord": "workspace:*", @@ -72,4 +73,4 @@ "ts-node": "10.9.2", "tsup": "8.3.5" } -} +} \ No newline at end of file diff --git a/agent/src/index.ts b/agent/src/index.ts index 53058cf4ec..8bae0af2b9 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -1,5 +1,6 @@ import { PostgresDatabaseAdapter } from "@elizaos/adapter-postgres"; import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite"; +import { PGLiteDatabaseAdapter } from "@elizaos/adapter-pglite"; import { AutoClientInterface } from "@elizaos/client-auto"; import { DiscordClientInterface } from "@elizaos/client-discord"; import { FarcasterAgentClient } from "@elizaos/client-farcaster"; @@ -375,6 +376,12 @@ function initializeDatabase(dataDir: string) { elizaLogger.error("Failed to connect to PostgreSQL:", error); }); + return db; + } else if (process.env.PGLITE_DATA_DIR) { + elizaLogger.info("Initializing PgLite adapter..."); + const db = new PGLiteDatabaseAdapter({ + dataDir: process.env.PGLITE_DATA_DIR, + }); return db; } else { const filePath = diff --git a/packages/adapter-pglite/src/index.ts b/packages/adapter-pglite/src/index.ts index cd0782032a..414fbd80a9 100644 --- a/packages/adapter-pglite/src/index.ts +++ b/packages/adapter-pglite/src/index.ts @@ -1,15 +1,5 @@ import { v4 } from "uuid"; -// Import the entire module as default -import pg from "pg"; -type Pool = pg.Pool; - -import { - QueryConfig, - QueryConfigValues, - QueryResult, - QueryResultRow, -} from "pg"; import { Account, Actor, @@ -28,7 +18,14 @@ import { import fs from "fs"; import { fileURLToPath } from "url"; import path from "path"; -import { PGlite, Results } from "@electric-sql/pglite"; +import { + PGlite, + PGliteOptions, + Results, + Transaction, +} from "@electric-sql/pglite"; +import { vector } from "@electric-sql/pglite/vector"; +import { fuzzystrmatch } from "@electric-sql/pglite/contrib/fuzzystrmatch"; const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file const __dirname = path.dirname(__filename); // get the name of the directory @@ -37,21 +34,23 @@ export class PGLiteDatabaseAdapter extends DatabaseAdapter implements IDatabaseCacheAdapter { - private readonly maxRetries: number = 3; - private readonly baseDelay: number = 1000; // 1 second - private readonly maxDelay: number = 10000; // 10 seconds - private readonly jitterMax: number = 1000; // 1 second - private readonly connectionTimeout: number = 5000; // 5 seconds - - constructor(db: PGlite) { + constructor(options: PGliteOptions) { super(); - this.db = db; + this.db = new PGlite({ + ...options, + // Add the vector and fuzzystrmatch extensions + extensions: { + ...(options.extensions ?? {}), + vector, + fuzzystrmatch, + }, + }); } async init() { await this.db.waitReady; - this.db.transaction(async (tx) => { + await this.withTransaction(async (tx) => { // Set application settings for embedding dimension const embeddingConfig = getEmbeddingConfig(); if (embeddingConfig.provider === EmbeddingProvider.OpenAI) { @@ -76,8 +75,8 @@ export class PGLiteDatabaseAdapter path.resolve(__dirname, "../schema.sql"), "utf8" ); - await tx.query(schema); - }); + await tx.exec(schema); + }, "init"); } async close() { @@ -89,72 +88,31 @@ export class PGLiteDatabaseAdapter context: string ): Promise { return this.withCircuitBreaker(async () => { - return this.withRetry(operation); + return operation(); }, context); } - private async withRetry(operation: () => Promise): Promise { - let lastError: Error = new Error("Unknown error"); // Initialize with default - - for (let attempt = 1; attempt <= this.maxRetries; attempt++) { - try { - return await operation(); - } catch (error) { - lastError = error as Error; - - if (attempt < this.maxRetries) { - // Calculate delay with exponential backoff - const backoffDelay = Math.min( - this.baseDelay * Math.pow(2, attempt - 1), - this.maxDelay - ); - - // Add jitter to prevent thundering herd - const jitter = Math.random() * this.jitterMax; - const delay = backoffDelay + jitter; - - elizaLogger.warn( - `Database operation failed (attempt ${attempt}/${this.maxRetries}):`, - { - error: - error instanceof Error - ? error.message - : String(error), - nextRetryIn: `${(delay / 1000).toFixed(1)}s`, - } - ); - - await new Promise((resolve) => setTimeout(resolve, delay)); - } else { - elizaLogger.error("Max retry attempts reached:", { - error: - error instanceof Error - ? error.message - : String(error), - totalAttempts: attempt, - }); - throw error instanceof Error - ? error - : new Error(String(error)); - } - } - } - - throw lastError; + private async withTransaction( + operation: (tx: Transaction) => Promise, + context: string + ): Promise { + return this.withCircuitBreaker(async () => { + return this.db.transaction(operation); + }, context); } - async query( + async query( queryTextOrConfig: string, - values?: QueryConfigValues + values?: any[] ): Promise> { return this.withDatabase(async () => { - return await this.db.query(queryTextOrConfig, values); + return await this.query(queryTextOrConfig, values); }, "query"); } async getRoom(roomId: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query<{ id: UUID }>( + const { rows } = await this.query<{ id: UUID }>( "SELECT id FROM rooms WHERE id = $1", [roomId] ); @@ -164,7 +122,7 @@ export class PGLiteDatabaseAdapter async getParticipantsForAccount(userId: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query( + const { rows } = await this.query( `SELECT id, "userId", "roomId", "last_message_read" FROM participants WHERE "userId" = $1`, @@ -179,7 +137,7 @@ export class PGLiteDatabaseAdapter userId: UUID ): Promise<"FOLLOWED" | "MUTED" | null> { return this.withDatabase(async () => { - const { rows } = await this.db.query<{ + const { rows } = await this.query<{ userState: "FOLLOWED" | "MUTED"; }>( `SELECT "userState" FROM participants WHERE "roomId" = $1 AND "userId" = $2`, @@ -208,7 +166,7 @@ export class PGLiteDatabaseAdapter queryParams = [...queryParams, params.agentId]; } - const { rows } = await this.db.query(query, queryParams); + const { rows } = await this.query(query, queryParams); return rows.map((row) => ({ ...row, content: @@ -225,7 +183,7 @@ export class PGLiteDatabaseAdapter state: "FOLLOWED" | "MUTED" | null ): Promise { return this.withDatabase(async () => { - await this.db.query( + await this.query( `UPDATE participants SET "userState" = $1 WHERE "roomId" = $2 AND "userId" = $3`, [state, roomId, userId] ); @@ -234,7 +192,7 @@ export class PGLiteDatabaseAdapter async getParticipantsForRoom(roomId: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query<{ userId: UUID }>( + const { rows } = await this.query<{ userId: UUID }>( 'SELECT "userId" FROM participants WHERE "roomId" = $1', [roomId] ); @@ -244,7 +202,7 @@ export class PGLiteDatabaseAdapter async getAccountById(userId: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query( + const { rows } = await this.query( "SELECT * FROM accounts WHERE id = $1", [userId] ); @@ -273,7 +231,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { const accountId = account.id ?? v4(); - await this.db.query( + await this.query( `INSERT INTO accounts (id, name, username, email, "avatarUrl", details) VALUES ($1, $2, $3, $4, $5, $6)`, [ @@ -303,7 +261,7 @@ export class PGLiteDatabaseAdapter async getActorById(params: { roomId: UUID }): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query( + const { rows } = await this.query( `SELECT a.id, a.name, a.username, a.details FROM participants p LEFT JOIN accounts a ON p."userId" = a.id @@ -350,7 +308,7 @@ export class PGLiteDatabaseAdapter async getMemoryById(id: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query( + const { rows } = await this.query( "SELECT * FROM memories WHERE id = $1", [id] ); @@ -388,7 +346,7 @@ export class PGLiteDatabaseAdapter isUnique = similarMemories.length === 0; } - await this.db.query( + await this.query( `INSERT INTO memories ( id, type, content, embedding, "userId", "roomId", "agentId", "unique", "createdAt" ) VALUES ($1, $2, $3, $4, $5::uuid, $6::uuid, $7::uuid, $8, to_timestamp($9/1000.0))`, @@ -497,7 +455,7 @@ export class PGLiteDatabaseAdapter limit: params.count, }); - const { rows } = await this.db.query(sql, values); + const { rows } = await this.query(sql, values); return rows.map((row) => ({ ...row, content: @@ -535,7 +493,7 @@ export class PGLiteDatabaseAdapter values.push(params.count); } - const { rows } = await this.db.query(sql, values); + const { rows } = await this.query(sql, values); return rows.map((row) => ({ ...row, objectives: @@ -549,7 +507,7 @@ export class PGLiteDatabaseAdapter async updateGoal(goal: Goal): Promise { return this.withDatabase(async () => { try { - await this.db.query( + await this.query( `UPDATE goals SET name = $1, status = $2, objectives = $3 WHERE id = $4`, [ goal.name, @@ -572,7 +530,7 @@ export class PGLiteDatabaseAdapter async createGoal(goal: Goal): Promise { return this.withDatabase(async () => { - await this.db.query( + await this.query( `INSERT INTO goals (id, "roomId", "userId", name, status, objectives) VALUES ($1, $2, $3, $4, $5, $6)`, [ @@ -592,7 +550,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { - const result = await this.db.query( + const result = await this.query( "DELETE FROM goals WHERE id = $1 RETURNING id", [goalId] ); @@ -615,9 +573,7 @@ export class PGLiteDatabaseAdapter async createRoom(roomId?: UUID): Promise { return this.withDatabase(async () => { const newRoomId = roomId || v4(); - await this.db.query("INSERT INTO rooms (id) VALUES ($1)", [ - newRoomId, - ]); + await this.query("INSERT INTO rooms (id) VALUES ($1)", [newRoomId]); return newRoomId as UUID; }, "createRoom"); } @@ -625,58 +581,53 @@ export class PGLiteDatabaseAdapter async removeRoom(roomId: UUID): Promise { if (!roomId) throw new Error("Room ID is required"); - return this.withDatabase(async () => { - await this.db.transaction(async (tx) => { - try { - // First check if room exists - const checkResult = await tx.query( - "SELECT id FROM rooms WHERE id = $1", - [roomId] - ); - - if (checkResult.rows.length === 0) { - elizaLogger.warn("No room found to remove:", { - roomId, - }); - throw new Error(`Room not found: ${roomId}`); - } + return this.withTransaction(async (tx) => { + try { + // First check if room exists + const checkResult = await tx.query( + "SELECT id FROM rooms WHERE id = $1", + [roomId] + ); - // Remove related data first (if not using CASCADE) - await tx.query('DELETE FROM memories WHERE "roomId" = $1', [ + if (checkResult.rows.length === 0) { + elizaLogger.warn("No room found to remove:", { roomId, - ]); - await tx.query( - 'DELETE FROM participants WHERE "roomId" = $1', - [roomId] - ); - await tx.query('DELETE FROM goals WHERE "roomId" = $1', [ - roomId, - ]); + }); + throw new Error(`Room not found: ${roomId}`); + } - // Finally remove the room - const result = await tx.query( - "DELETE FROM rooms WHERE id = $1 RETURNING id", - [roomId] - ); + // Remove related data first (if not using CASCADE) + await tx.query('DELETE FROM memories WHERE "roomId" = $1', [ + roomId, + ]); + await tx.query('DELETE FROM participants WHERE "roomId" = $1', [ + roomId, + ]); + await tx.query('DELETE FROM goals WHERE "roomId" = $1', [ + roomId, + ]); - elizaLogger.debug( - "Room and related data removed successfully:", - { - roomId, - removed: result?.affectedRows ?? 0 > 0, - } - ); - } catch (error) { - elizaLogger.error("Failed to remove room:", { + // Finally remove the room + const result = await tx.query( + "DELETE FROM rooms WHERE id = $1 RETURNING id", + [roomId] + ); + + elizaLogger.debug( + "Room and related data removed successfully:", + { roomId, - error: - error instanceof Error - ? error.message - : String(error), - }); - throw error; - } - }); + removed: result?.affectedRows ?? 0 > 0, + } + ); + } catch (error) { + elizaLogger.error("Failed to remove room:", { + roomId, + error: + error instanceof Error ? error.message : String(error), + }); + throw error; + } }, "removeRoom"); } @@ -692,7 +643,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { const relationshipId = v4(); - await this.db.query( + await this.query( `INSERT INTO relationships (id, "userA", "userB", "userId") VALUES ($1, $2, $3, $4) RETURNING id`, @@ -743,7 +694,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { - const { rows } = await this.db.query( + const { rows } = await this.query( `SELECT * FROM relationships WHERE ("userA" = $1 AND "userB" = $2) OR ("userA" = $2 AND "userB" = $1)`, @@ -783,7 +734,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { - const { rows } = await this.db.query( + const { rows } = await this.query( `SELECT * FROM relationships WHERE "userA" = $1 OR "userB" = $1 ORDER BY "createdAt" DESC`, // Add ordering if you have this field @@ -863,7 +814,10 @@ export class PGLiteDatabaseAdapter LIMIT $5 `; - const { rows } = await this.db.query<{ embedding: number[], levenshtein_score: number }>(sql, [ + const { rows } = await this.query<{ + embedding: number[]; + levenshtein_score: number; + }>(sql, [ opts.query_input, opts.query_field_name, opts.query_field_sub_name, @@ -932,7 +886,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { try { const logId = v4(); // Generate ID for tracking - await this.db.query( + await this.query( `INSERT INTO logs ( id, body, @@ -1061,7 +1015,7 @@ export class PGLiteDatabaseAdapter values.push(params.count); } - const { rows } = await this.db.query(sql, values); + const { rows } = await this.query(sql, values); return rows.map((row) => ({ ...row, content: @@ -1076,7 +1030,7 @@ export class PGLiteDatabaseAdapter async addParticipant(userId: UUID, roomId: UUID): Promise { return this.withDatabase(async () => { try { - await this.db.query( + await this.query( `INSERT INTO participants (id, "userId", "roomId") VALUES ($1, $2, $3)`, [v4(), userId, roomId] @@ -1092,7 +1046,7 @@ export class PGLiteDatabaseAdapter async removeParticipant(userId: UUID, roomId: UUID): Promise { return this.withDatabase(async () => { try { - await this.db.query( + await this.query( `DELETE FROM participants WHERE "userId" = $1 AND "roomId" = $2`, [userId, roomId] ); @@ -1109,7 +1063,7 @@ export class PGLiteDatabaseAdapter status: GoalStatus; }): Promise { return this.withDatabase(async () => { - await this.db.query("UPDATE goals SET status = $1 WHERE id = $2", [ + await this.query("UPDATE goals SET status = $1 WHERE id = $2", [ params.status, params.goalId, ]); @@ -1118,7 +1072,7 @@ export class PGLiteDatabaseAdapter async removeMemory(memoryId: UUID, tableName: string): Promise { return this.withDatabase(async () => { - await this.db.query( + await this.query( "DELETE FROM memories WHERE type = $1 AND id = $2", [tableName, memoryId] ); @@ -1127,7 +1081,7 @@ export class PGLiteDatabaseAdapter async removeAllMemories(roomId: UUID, tableName: string): Promise { return this.withDatabase(async () => { - await this.db.query( + await this.query( `DELETE FROM memories WHERE type = $1 AND "roomId" = $2`, [tableName, roomId] ); @@ -1147,7 +1101,7 @@ export class PGLiteDatabaseAdapter sql += ` AND "unique" = true`; } - const { rows } = await this.db.query<{ count: number }>(sql, [ + const { rows } = await this.query<{ count: number }>(sql, [ tableName, roomId, ]); @@ -1157,15 +1111,13 @@ export class PGLiteDatabaseAdapter async removeAllGoals(roomId: UUID): Promise { return this.withDatabase(async () => { - await this.db.query(`DELETE FROM goals WHERE "roomId" = $1`, [ - roomId, - ]); + await this.query(`DELETE FROM goals WHERE "roomId" = $1`, [roomId]); }, "removeAllGoals"); } async getRoomsForParticipant(userId: UUID): Promise { return this.withDatabase(async () => { - const { rows } = await this.db.query<{ roomId: UUID }>( + const { rows } = await this.query<{ roomId: UUID }>( `SELECT "roomId" FROM participants WHERE "userId" = $1`, [userId] ); @@ -1176,7 +1128,7 @@ export class PGLiteDatabaseAdapter async getRoomsForParticipants(userIds: UUID[]): Promise { return this.withDatabase(async () => { const placeholders = userIds.map((_, i) => `$${i + 1}`).join(", "); - const { rows } = await this.db.query<{ roomId: UUID }>( + const { rows } = await this.query<{ roomId: UUID }>( `SELECT DISTINCT "roomId" FROM participants WHERE "userId" IN (${placeholders})`, userIds ); @@ -1204,7 +1156,7 @@ export class PGLiteDatabaseAdapter ORDER BY a.name `; - const result = await this.db.query(sql, [params.roomId]); + const result = await this.query(sql, [params.roomId]); elizaLogger.debug("Retrieved actor details:", { roomId: params.roomId, @@ -1276,79 +1228,59 @@ export class PGLiteDatabaseAdapter agentId: UUID; value: string; }): Promise { - return this.withDatabase(async () => { - return ( - (await this.db.transaction(async (tx) => { - try { - try { - await tx.query( - `INSERT INTO cache ("key", "agentId", "value", "createdAt") + return ( + (await this.withTransaction(async (tx) => { + try { + await tx.query( + `INSERT INTO cache ("key", "agentId", "value", "createdAt") VALUES ($1, $2, $3, CURRENT_TIMESTAMP) ON CONFLICT ("key", "agentId") DO UPDATE SET "value" = EXCLUDED.value, "createdAt" = CURRENT_TIMESTAMP`, - [params.key, params.agentId, params.value] - ); - return true; - } catch (error) { - await tx.rollback(); - elizaLogger.error("Error setting cache", { - error: - error instanceof Error - ? error.message - : String(error), - key: params.key, - agentId: params.agentId, - }); - return false; - } - } catch (error) { - elizaLogger.error( - "Database connection error in setCache", - error - ); - return false; - } - })) ?? false - ); - }, "setCache"); + [params.key, params.agentId, params.value] + ); + return true; + } catch (error) { + await tx.rollback(); + elizaLogger.error("Error setting cache", { + error: + error instanceof Error + ? error.message + : String(error), + key: params.key, + agentId: params.agentId, + }); + return false; + } + }, "setCache")) ?? false + ); } async deleteCache(params: { key: string; agentId: UUID; }): Promise { - return this.withDatabase(async () => { - return ( - (await this.db.transaction(async (tx) => { - try { - try { - await tx.query( - `DELETE FROM cache WHERE "key" = $1 AND "agentId" = $2`, - [params.key, params.agentId] - ); - return true; - } catch (error) { - tx.rollback(); - elizaLogger.error("Error deleting cache", { - error: - error instanceof Error - ? error.message - : String(error), - key: params.key, - agentId: params.agentId, - }); - return false; - } - } catch (error) { - elizaLogger.error( - "Database connection error in deleteCache", - error - ); - return false; - } - })) ?? false - ); - }, "deleteCache"); + return ( + (await this.withTransaction(async (tx) => { + try { + await tx.query( + `DELETE FROM cache WHERE "key" = $1 AND "agentId" = $2`, + [params.key, params.agentId] + ); + return true; + } catch (error) { + tx.rollback(); + elizaLogger.error("Error deleting cache", { + error: + error instanceof Error + ? error.message + : String(error), + key: params.key, + agentId: params.agentId, + }); + return false; + } + }, "deleteCache")) ?? false + ); } } diff --git a/packages/core/src/test_resources/createRuntime.ts b/packages/core/src/test_resources/createRuntime.ts index 209b800cbe..35ac810e01 100644 --- a/packages/core/src/test_resources/createRuntime.ts +++ b/packages/core/src/test_resources/createRuntime.ts @@ -4,6 +4,7 @@ import { } from "@elizaos/adapter-sqlite"; import { SqlJsDatabaseAdapter } from "@elizaos/adapter-sqljs"; import { SupabaseDatabaseAdapter } from "@elizaos/adapter-supabase"; +import { PGLiteDatabaseAdapter } from "@elizaos/adapter-pglite"; import { DatabaseAdapter } from "../database.ts"; import { getEndpoint } from "../models.ts"; import { AgentRuntime } from "../runtime.ts"; @@ -117,6 +118,23 @@ export async function createRuntime({ ); break; } + case "pglite": + { + // Import the PGLite adapter + await import("@electric-sql/pglite"); + + // PGLite adapter + adapter = new PGLiteDatabaseAdapter({ dataDir: "../pglite" }); + + // Create a test user and session + session = { + user: { + id: zeroUuid, + email: "test@example.com", + }, + }; + } + break; case "sqlite": default: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc9fac569d..c959af4e41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,6 +102,9 @@ importers: agent: dependencies: + '@elizaos/adapter-pglite': + specifier: workspace:* + version: link:../packages/adapter-pglite '@elizaos/adapter-postgres': specifier: workspace:* version: link:../packages/adapter-postgres @@ -368,6 +371,9 @@ importers: '@docusaurus/preset-classic': specifier: 3.6.3 version: 3.6.3(@algolia/client-search@5.18.0)(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/react@18.3.12)(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@docusaurus/theme-common': + specifier: 3.6.3 + version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) '@docusaurus/theme-mermaid': specifier: 3.6.3 version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10) @@ -383,6 +389,9 @@ importers: dotenv: specifier: ^16.4.7 version: 16.4.7 + lunr: + specifier: 2.3.9 + version: 2.3.9 prism-react-renderer: specifier: 2.3.1 version: 2.3.1(react@18.3.1) @@ -1172,25 +1181,6 @@ importers: specifier: 7.1.0 version: 7.1.0 - packages/plugin-ferePro: - dependencies: - '@elizaos/core': - specifier: ^0.1.7-alpha.1 - version: 0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1) - tsup: - specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) - ws: - specifier: ^8.18.0 - version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - devDependencies: - '@types/ws': - specifier: ^8.5.13 - version: 8.5.13 - tsx: - specifier: ^4.19.2 - version: 4.19.2 - packages/plugin-flow: dependencies: '@elizaos/core': @@ -3890,9 +3880,6 @@ packages: '@electric-sql/pglite@0.2.15': resolution: {integrity: sha512-Jiq31Dnk+rg8rMhcSxs4lQvHTyizNo5b269c1gCC3ldQ0sCLrNVPGzy+KnmonKy1ZArTUuXZf23/UamzFMKVaA==} - '@elizaos/core@0.1.7-alpha.2': - resolution: {integrity: sha512-gNvFw/Xnv4dlcfmmKxRa+baKq6en4TitAjUGvo8LgAUkSk156A0fffJ0lAsc1rX8zMB5NsIqdvMCbwKxDd54OQ==} - '@emnapi/core@1.3.1': resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} @@ -23043,56 +23030,6 @@ snapshots: '@electric-sql/pglite@0.2.15': {} - '@elizaos/core@0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)': - dependencies: - '@ai-sdk/anthropic': 0.0.56(zod@3.23.8) - '@ai-sdk/google': 0.0.55(zod@3.23.8) - '@ai-sdk/google-vertex': 0.0.43(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(zod@3.23.8) - '@ai-sdk/groq': 0.0.3(zod@3.23.8) - '@ai-sdk/openai': 1.0.5(zod@3.23.8) - '@anthropic-ai/sdk': 0.30.1(encoding@0.1.13) - '@fal-ai/client': 1.2.0 - '@types/uuid': 10.0.0 - ai: 3.4.33(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8) - anthropic-vertex-ai: 1.0.2(encoding@0.1.13)(zod@3.23.8) - fastembed: 1.14.1 - fastestsmallesttextencoderdecoder: 1.0.22 - gaxios: 6.7.1(encoding@0.1.13) - glob: 11.0.0 - handlebars: 4.7.8 - js-sha1: 0.7.0 - js-tiktoken: 1.0.15 - langchain: 0.3.6(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) - ollama-ai-provider: 0.16.1(zod@3.23.8) - openai: 4.73.0(encoding@0.1.13)(zod@3.23.8) - tinyld: 1.3.4 - together-ai: 0.7.0(encoding@0.1.13) - unique-names-generator: 4.7.1 - uuid: 11.0.3 - zod: 3.23.8 - transitivePeerDependencies: - - '@google-cloud/vertexai' - - '@langchain/anthropic' - - '@langchain/aws' - - '@langchain/cohere' - - '@langchain/core' - - '@langchain/google-genai' - - '@langchain/google-vertexai' - - '@langchain/groq' - - '@langchain/mistralai' - - '@langchain/ollama' - - axios - - cheerio - - encoding - - peggy - - react - - solid-js - - sswr - - supports-color - - svelte - - typeorm - - vue - '@emnapi/core@1.3.1': dependencies: '@emnapi/wasi-threads': 1.0.1 @@ -32921,6 +32858,7 @@ snapshots: '@esbuild/win32-arm64': 0.23.1 '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + optional: true esbuild@0.24.2: optionalDependencies: @@ -34000,6 +33938,7 @@ snapshots: get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 + optional: true get-uri@6.0.4: dependencies: @@ -40574,7 +40513,8 @@ snapshots: resolve-pathname@3.0.0: {} - resolve-pkg-maps@1.0.0: {} + resolve-pkg-maps@1.0.0: + optional: true resolve.exports@2.0.3: {} @@ -42197,6 +42137,7 @@ snapshots: get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 + optional: true tty-browserify@0.0.1: {} From d9bc04d0b42acb22a6a288401cd0193f46742b7e Mon Sep 17 00:00:00 2001 From: KONFeature Date: Sat, 4 Jan 2025 12:51:04 +0100 Subject: [PATCH 22/58] chore: lint --- agent/src/index.ts | 1 + packages/adapter-pglite/src/index.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/agent/src/index.ts b/agent/src/index.ts index 8bae0af2b9..d01a31e437 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -379,6 +379,7 @@ function initializeDatabase(dataDir: string) { return db; } else if (process.env.PGLITE_DATA_DIR) { elizaLogger.info("Initializing PgLite adapter..."); + // `dataDir: memory://` for in memory pg const db = new PGLiteDatabaseAdapter({ dataDir: process.env.PGLITE_DATA_DIR, }); diff --git a/packages/adapter-pglite/src/index.ts b/packages/adapter-pglite/src/index.ts index 414fbd80a9..5f65ff7989 100644 --- a/packages/adapter-pglite/src/index.ts +++ b/packages/adapter-pglite/src/index.ts @@ -101,12 +101,12 @@ export class PGLiteDatabaseAdapter }, context); } - async query( + async query( queryTextOrConfig: string, - values?: any[] + values?: unknown[] ): Promise> { return this.withDatabase(async () => { - return await this.query(queryTextOrConfig, values); + return await this.db.query(queryTextOrConfig, values); }, "query"); } @@ -400,7 +400,7 @@ export class PGLiteDatabaseAdapter return this.withDatabase(async () => { // Build query let sql = `SELECT * FROM memories WHERE type = $1 AND "roomId" = $2`; - const values: any[] = [params.tableName, params.roomId]; + const values: unknown[] = [params.tableName, params.roomId]; let paramCount = 2; // Add time range filters @@ -474,7 +474,7 @@ export class PGLiteDatabaseAdapter }): Promise { return this.withDatabase(async () => { let sql = `SELECT * FROM goals WHERE "roomId" = $1`; - const values: any[] = [params.roomId]; + const values: unknown[] = [params.roomId]; let paramCount = 1; if (params.userId) { @@ -974,7 +974,7 @@ export class PGLiteDatabaseAdapter WHERE type = $2 `; - const values: any[] = [vectorStr, params.tableName]; + const values: unknown[] = [vectorStr, params.tableName]; // Log the query for debugging elizaLogger.debug("Query debug:", { From 2931fb5dbc9d903d073ea8ec4436cd6e79282bab Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Sat, 4 Jan 2025 13:55:29 +0100 Subject: [PATCH 23/58] feat: add binance plugin --- agent/package.json | 3 +- agent/src/index.ts | 31 +-- packages/plugin-binance/package.json | 35 +++ .../plugin-binance/src/actions/priceCheck.ts | 163 ++++++++++++++ .../plugin-binance/src/actions/spotBalance.ts | 180 +++++++++++++++ .../plugin-binance/src/actions/spotTrade.ts | 182 ++++++++++++++++ packages/plugin-binance/src/environment.ts | 32 +++ packages/plugin-binance/src/index.ts | 14 ++ .../src/services/BinanceService.ts | 206 ++++++++++++++++++ packages/plugin-binance/src/types.ts | 85 ++++++++ packages/plugin-binance/tsconfig.json | 13 ++ packages/plugin-binance/tsup.config.ts | 24 ++ pnpm-lock.yaml | 119 ++++------ 13 files changed, 1000 insertions(+), 87 deletions(-) create mode 100644 packages/plugin-binance/package.json create mode 100644 packages/plugin-binance/src/actions/priceCheck.ts create mode 100644 packages/plugin-binance/src/actions/spotBalance.ts create mode 100644 packages/plugin-binance/src/actions/spotTrade.ts create mode 100644 packages/plugin-binance/src/environment.ts create mode 100644 packages/plugin-binance/src/index.ts create mode 100644 packages/plugin-binance/src/services/BinanceService.ts create mode 100644 packages/plugin-binance/src/types.ts create mode 100644 packages/plugin-binance/tsconfig.json create mode 100644 packages/plugin-binance/tsup.config.ts diff --git a/agent/package.json b/agent/package.json index f4fa0f33e0..bce8133c01 100644 --- a/agent/package.json +++ b/agent/package.json @@ -33,6 +33,7 @@ "@elizaos/plugin-0g": "workspace:*", "@elizaos/plugin-abstract": "workspace:*", "@elizaos/plugin-aptos": "workspace:*", + "@elizaos/plugin-binance": "workspace:*", "@elizaos/plugin-bootstrap": "workspace:*", "@elizaos/plugin-intiface": "workspace:*", "@elizaos/plugin-coinbase": "workspace:*", @@ -72,4 +73,4 @@ "ts-node": "10.9.2", "tsup": "8.3.5" } -} +} \ No newline at end of file diff --git a/agent/src/index.ts b/agent/src/index.ts index 53058cf4ec..b8d100a183 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -1,4 +1,5 @@ import { PostgresDatabaseAdapter } from "@elizaos/adapter-postgres"; +import { RedisClient } from "@elizaos/adapter-redis"; import { SqliteDatabaseAdapter } from "@elizaos/adapter-sqlite"; import { AutoClientInterface } from "@elizaos/client-auto"; import { DiscordClientInterface } from "@elizaos/client-discord"; @@ -10,31 +11,33 @@ import { TwitterClientInterface } from "@elizaos/client-twitter"; import { AgentRuntime, CacheManager, + CacheStore, Character, + Client, Clients, DbCacheAdapter, defaultCharacter, elizaLogger, FsCacheAdapter, IAgentRuntime, + ICacheManager, IDatabaseAdapter, IDatabaseCacheAdapter, ModelProviderName, settings, stringToUuid, validateCharacterConfig, - CacheStore, - Client, - ICacheManager, - parseBooleanFromText, } from "@elizaos/core"; -import { RedisClient } from "@elizaos/adapter-redis"; import { zgPlugin } from "@elizaos/plugin-0g"; import { bootstrapPlugin } from "@elizaos/plugin-bootstrap"; import createGoatPlugin from "@elizaos/plugin-goat"; // import { intifacePlugin } from "@elizaos/plugin-intiface"; import { DirectClient } from "@elizaos/client-direct"; +import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; +import { abstractPlugin } from "@elizaos/plugin-abstract"; import { aptosPlugin } from "@elizaos/plugin-aptos"; +import { avalanchePlugin } from "@elizaos/plugin-avalanche"; +import { binancePlugin } from "@elizaos/plugin-binance"; import { advancedTradePlugin, coinbaseCommercePlugin, @@ -44,32 +47,30 @@ import { webhookPlugin, } from "@elizaos/plugin-coinbase"; import { confluxPlugin } from "@elizaos/plugin-conflux"; +import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; +import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; import { evmPlugin } from "@elizaos/plugin-evm"; -import { storyPlugin } from "@elizaos/plugin-story"; import { flowPlugin } from "@elizaos/plugin-flow"; import { fuelPlugin } from "@elizaos/plugin-fuel"; import { imageGenerationPlugin } from "@elizaos/plugin-image-generation"; -import { ThreeDGenerationPlugin } from "@elizaos/plugin-3d-generation"; import { multiversxPlugin } from "@elizaos/plugin-multiversx"; import { nearPlugin } from "@elizaos/plugin-near"; import { nftGenerationPlugin } from "@elizaos/plugin-nft-generation"; import { createNodePlugin } from "@elizaos/plugin-node"; import { solanaPlugin } from "@elizaos/plugin-solana"; +import { storyPlugin } from "@elizaos/plugin-story"; import { suiPlugin } from "@elizaos/plugin-sui"; import { TEEMode, teePlugin } from "@elizaos/plugin-tee"; import { tonPlugin } from "@elizaos/plugin-ton"; -import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; -import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; -import { abstractPlugin } from "@elizaos/plugin-abstract"; -import { avalanchePlugin } from "@elizaos/plugin-avalanche"; import { webSearchPlugin } from "@elizaos/plugin-web-search"; -import { echoChamberPlugin } from "@elizaos/plugin-echochambers"; +import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; + import Database from "better-sqlite3"; import fs from "fs"; +import net from "net"; import path from "path"; import { fileURLToPath } from "url"; import yargs from "yargs"; -import net from "net"; const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file const __dirname = path.dirname(__filename); // get the name of the directory @@ -588,6 +589,10 @@ export async function createAgent( getSecret(character, "ABSTRACT_PRIVATE_KEY") ? abstractPlugin : null, + getSecret(character, "BINANCE_API_KEY") && + getSecret(character, "BINANCE_SECRET_KEY") + ? binancePlugin + : null, getSecret(character, "FLOW_ADDRESS") && getSecret(character, "FLOW_PRIVATE_KEY") ? flowPlugin diff --git a/packages/plugin-binance/package.json b/packages/plugin-binance/package.json new file mode 100644 index 0000000000..1f8bbeee11 --- /dev/null +++ b/packages/plugin-binance/package.json @@ -0,0 +1,35 @@ +{ + "name": "@elizaos/plugin-binance", + "version": "0.1.0", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "@elizaos/source": "./src/index.ts", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + } + }, + "files": [ + "dist" + ], + "dependencies": { + "@elizaos/core": "workspace:*", + "@binance/connector": "^3.6.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "tsup": "8.3.5", + "@types/node": "^20.0.0" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache ." + } +} \ No newline at end of file diff --git a/packages/plugin-binance/src/actions/priceCheck.ts b/packages/plugin-binance/src/actions/priceCheck.ts new file mode 100644 index 0000000000..d118140ab4 --- /dev/null +++ b/packages/plugin-binance/src/actions/priceCheck.ts @@ -0,0 +1,163 @@ +import { + ActionExample, + composeContext, + elizaLogger, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { BinanceService } from "../services/BinanceService"; + +const priceCheckTemplate = `Look at ONLY your LAST RESPONSE message in this conversation, where you just said which cryptocurrency price you would check. +Based on ONLY that last message, provide the trading symbol. + +For example: +- If your last message was "I'll check the current Ethereum price..." -> return "ETH" +- If your last message was "I'll check the current Solana price..." -> return "SOL" +- If your last message was "I'll check the current Bitcoin price..." -> return "BTC" + +\`\`\`json +{ + "symbol": "", + "quoteCurrency": "" +} +\`\`\` + +Last part of conversation: +{{recentMessages}}`; + +export const priceCheck: Action = { + name: "GET_PRICE", + similes: [ + "CHECK_PRICE", + "PRICE_CHECK", + "GET_CRYPTO_PRICE", + "CRYPTO_PRICE", + "CHECK_CRYPTO_PRICE", + "PRICE_LOOKUP", + "CURRENT_PRICE", + ], + description: "Get current price information for a cryptocurrency pair", + validate: async () => true, // Public endpoint + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: Record, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting Binance GET_PRICE handler..."); + + try { + // Initialize or update state + state = !state + ? await runtime.composeState(message) + : await runtime.updateRecentMessageState(state); + + const context = composeContext({ + state, + template: priceCheckTemplate, + }); + + const rawContent = await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + }); + + if (!rawContent?.symbol) { + throw new Error( + "Could not determine which cryptocurrency to check" + ); + } + + // Ensure the content has the required shape + const content = { + symbol: rawContent.symbol.toString().toUpperCase().trim(), + quoteCurrency: (rawContent.quoteCurrency || "USDT") + .toString() + .toUpperCase() + .trim(), + }; + + if (content.symbol.length < 2 || content.symbol.length > 10) { + throw new Error("Invalid cryptocurrency symbol"); + } + + const binanceService = new BinanceService(); + const priceData = await binanceService.getPrice(content); + + if (callback) { + callback({ + text: `The current ${content.symbol} price is ${BinanceService.formatPrice(priceData.price)} ${content.quoteCurrency}`, + content: priceData, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error in price check:", error); + if (callback) { + const errorMessage = error.message.includes("Invalid API key") + ? "Unable to connect to Binance API" + : error.message.includes("Invalid symbol") + ? `Sorry, could not find price for the cryptocurrency symbol you provided` + : `Sorry, I encountered an error: ${error.message}`; + + callback({ + text: errorMessage, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "What's the current price of Bitcoin?", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll check the current Bitcoin price for you right away.", + action: "GET_PRICE", + }, + }, + { + user: "{{agent}}", + content: { + text: "The current BTC price is 42,150.25 USDT", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Can you check ETH price in EUR?", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll fetch the current Ethereum price in euros for you.", + action: "GET_PRICE", + }, + }, + { + user: "{{agent}}", + content: { + text: "The current ETH price is 2,245.80 EUR", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-binance/src/actions/spotBalance.ts b/packages/plugin-binance/src/actions/spotBalance.ts new file mode 100644 index 0000000000..af0e755f82 --- /dev/null +++ b/packages/plugin-binance/src/actions/spotBalance.ts @@ -0,0 +1,180 @@ +import { + ActionExample, + composeContext, + elizaLogger, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { validateBinanceConfig } from "../environment"; +import { BinanceService } from "../services/BinanceService"; +import { BalanceCheckRequest } from "../types"; + +const spotBalanceTemplate = `Look at ONLY your LAST RESPONSE message in this conversation, where you just confirmed which cryptocurrency balance to check. +Based on ONLY that last message, extract the cryptocurrency symbol. + +For example: +- If your last message was "I'll fetch your Solana wallet balance..." -> return "SOL" +- If your last message was "I'll check your BTC balance..." -> return "BTC" +- If your last message was "I'll get your ETH balance..." -> return "ETH" + +\`\`\`json +{ + "asset": "" +} +\`\`\` + +Last part of conversation: +{{recentMessages}}`; + +export const spotBalance: Action = { + name: "GET_SPOT_BALANCE", + similes: [ + "CHECK_BALANCE", + "BALANCE_CHECK", + "GET_WALLET_BALANCE", + "WALLET_BALANCE", + "CHECK_WALLET", + "VIEW_BALANCE", + "SHOW_BALANCE", + ], + description: "Get current spot wallet balance for one or all assets", + validate: async (runtime: IAgentRuntime) => { + try { + await validateBinanceConfig(runtime); + return true; + } catch (error) { + return false; + } + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting Binance GET_SPOT_BALANCE handler..."); + + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const balanceContext = composeContext({ + state, + template: spotBalanceTemplate, + }); + + const content = (await generateObjectDeprecated({ + runtime, + context: balanceContext, + modelClass: ModelClass.SMALL, + })) as BalanceCheckRequest; + + try { + const binanceService = new BinanceService({ + apiKey: runtime.getSetting("BINANCE_API_KEY"), + secretKey: runtime.getSetting("BINANCE_SECRET_KEY"), + }); + + const balanceData = await binanceService.getBalance(content); + + if (content.asset) { + const assetBalance = balanceData.balances[0]; + if (assetBalance) { + if (callback) { + callback({ + text: `${content.asset} Balance:\nAvailable: ${assetBalance.free}\nLocked: ${assetBalance.locked}`, + content: assetBalance, + }); + } + } else { + if (callback) { + callback({ + text: `No balance found for ${content.asset}`, + content: { error: "Asset not found" }, + }); + } + } + } else { + const balanceText = balanceData.balances + .map( + (b) => + `${b.asset}: Available: ${b.free}, Locked: ${b.locked}` + ) + .join("\n"); + + if (callback) { + callback({ + text: `Spot Wallet Balances:\n${balanceText}`, + content: balanceData.balances, + }); + } + } + + return true; + } catch (error) { + elizaLogger.error("Error in balance check:", { + message: error.message, + code: error.code, + }); + if (callback) { + callback({ + text: error.message, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "What's my current Bitcoin balance?", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll check your BTC balance for you.", + action: "GET_SPOT_BALANCE", + }, + }, + { + user: "{{agent}}", + content: { + text: "BTC Balance:\nAvailable: 0.5\nLocked: 0.1", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Show me all my wallet balances", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll fetch all your spot wallet balances.", + action: "GET_SPOT_BALANCE", + }, + }, + { + user: "{{agent}}", + content: { + text: "Spot Wallet Balances:\nBTC: Available: 0.5, Locked: 0.1\nETH: Available: 2.0, Locked: 0.0\nUSDT: Available: 1000.0, Locked: 0.0", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-binance/src/actions/spotTrade.ts b/packages/plugin-binance/src/actions/spotTrade.ts new file mode 100644 index 0000000000..e763385d07 --- /dev/null +++ b/packages/plugin-binance/src/actions/spotTrade.ts @@ -0,0 +1,182 @@ +import { + ActionExample, + composeContext, + elizaLogger, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, + type Action, +} from "@elizaos/core"; +import { BinanceService } from "../services/BinanceService"; +import { SpotTradeSchema } from "../types"; + +const spotTradeTemplate = `Look at your LAST RESPONSE in the conversation where you confirmed a trade/swap request. +Based on ONLY that last message, extract the trading details: + +Trading pairs on Binance must include USDT or BUSD or USDC. For example: +- For "swap SOL for USDC" -> use "SOLUSDC" as symbol +- For "swap ETH for USDT" -> use "ETHUSDT" as symbol +- For "buy BTC with USDT" -> use "BTCUSDT" as symbol + +\`\`\`json +{ + "symbol": "", + "side": "SELL", + "type": "MARKET", + "quantity": "" +} +\`\`\` + +Recent conversation: +{{recentMessages}}`; + +export const spotTrade: Action = { + name: "EXECUTE_SPOT_TRADE", + similes: [ + "SPOT_TRADE", + "MARKET_ORDER", + "LIMIT_ORDER", + "BUY_CRYPTO", + "SELL_CRYPTO", + "PLACE_ORDER", + ], + description: "Execute a spot trade on Binance", + validate: async (runtime: IAgentRuntime) => { + return !!( + runtime.getSetting("BINANCE_API_KEY") && + runtime.getSetting("BINANCE_SECRET_KEY") + ); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: Record, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting EXECUTE_SPOT_TRADE handler..."); + + let content; + try { + state = !state + ? await runtime.composeState(message) + : await runtime.updateRecentMessageState(state); + + const context = composeContext({ + state, + template: spotTradeTemplate, + }); + + content = await generateObjectDeprecated({ + runtime, + context, + modelClass: ModelClass.SMALL, + }); + + elizaLogger.log("Generated content:", content); + + // Convert quantity to number if it's a string + if (content && typeof content.quantity === "string") { + content.quantity = parseFloat(content.quantity); + } + + elizaLogger.log("Attempting to parse content:", { + originalContent: content, + quantity: content?.quantity, + quantityType: content?.quantity + ? typeof content.quantity + : "undefined", + }); + + const parseResult = SpotTradeSchema.safeParse(content); + if (!parseResult.success) { + throw new Error( + `Invalid spot trade content: ${JSON.stringify(parseResult.error.errors, null, 2)}` + ); + } + + const binanceService = new BinanceService({ + apiKey: runtime.getSetting("BINANCE_API_KEY"), + secretKey: runtime.getSetting("BINANCE_SECRET_KEY"), + }); + + const tradeResult = await binanceService.executeTrade(content); + + if (callback) { + const orderType = + content.type === "MARKET" + ? "market" + : `limit at ${BinanceService.formatPrice(content.price!)}`; + + callback({ + text: `Successfully placed a ${orderType} order to ${content.side.toLowerCase()} ${content.quantity} ${content.symbol}\nOrder ID: ${tradeResult.orderId}\nStatus: ${tradeResult.status}`, + content: tradeResult, + }); + } + + return true; + } catch (error) { + elizaLogger.error("Error executing trade:", { + error, + content, + message: error.message, + name: error.name, + stack: error.stack, + }); + if (callback) { + callback({ + text: `Error executing trade: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Buy 0.1 BTC at market price", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll execute a market order to buy 0.1 BTC now.", + action: "EXECUTE_SPOT_TRADE", + }, + }, + { + user: "{{agent}}", + content: { + text: "Successfully placed a market order to buy 0.1 BTCUSDT\nOrder ID: 123456789\nStatus: FILLED", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Place a limit order to sell 100 BNB at 250 USDT", + }, + }, + { + user: "{{agent}}", + content: { + text: "I'll place a limit order to sell 100 BNB at 250 USDT.", + action: "EXECUTE_SPOT_TRADE", + }, + }, + { + user: "{{agent}}", + content: { + text: "Successfully placed a limit order to sell 100 BNBUSDT at 250\nOrder ID: 987654321\nStatus: NEW", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-binance/src/environment.ts b/packages/plugin-binance/src/environment.ts new file mode 100644 index 0000000000..9d56e3e7d9 --- /dev/null +++ b/packages/plugin-binance/src/environment.ts @@ -0,0 +1,32 @@ +import { IAgentRuntime } from "@elizaos/core"; +import { z } from "zod"; + +export const binanceEnvSchema = z.object({ + BINANCE_API_KEY: z.string().min(1, "Binance API key is required"), + BINANCE_SECRET_KEY: z.string().min(1, "Binance secret key is required"), +}); + +export type BinanceConfig = z.infer; + +export async function validateBinanceConfig( + runtime: IAgentRuntime +): Promise { + try { + const config = { + BINANCE_API_KEY: runtime.getSetting("BINANCE_API_KEY"), + BINANCE_SECRET_KEY: runtime.getSetting("BINANCE_SECRET_KEY"), + }; + + return binanceEnvSchema.parse(config); + } catch (error) { + if (error instanceof z.ZodError) { + const errorMessages = error.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join("\n"); + throw new Error( + `Binance configuration validation failed:\n${errorMessages}` + ); + } + throw error; + } +} diff --git a/packages/plugin-binance/src/index.ts b/packages/plugin-binance/src/index.ts new file mode 100644 index 0000000000..c8922831ee --- /dev/null +++ b/packages/plugin-binance/src/index.ts @@ -0,0 +1,14 @@ +import { Plugin } from "@elizaos/core"; +import { priceCheck } from "./actions/priceCheck"; +import { spotBalance } from "./actions/spotBalance"; +import { spotTrade } from "./actions/spotTrade"; + +export const binancePlugin: Plugin = { + name: "binance", + description: "Binance Plugin for Eliza", + actions: [spotTrade, priceCheck, spotBalance], + evaluators: [], + providers: [], +}; + +export default binancePlugin; diff --git a/packages/plugin-binance/src/services/BinanceService.ts b/packages/plugin-binance/src/services/BinanceService.ts new file mode 100644 index 0000000000..1fda55544c --- /dev/null +++ b/packages/plugin-binance/src/services/BinanceService.ts @@ -0,0 +1,206 @@ +// services/BinanceService.ts +import { Spot } from "@binance/connector"; +import { elizaLogger } from "@elizaos/core"; +import { + BalanceCheckRequest, + BalanceResponse, + BinanceConfig, + BinanceError, + PriceCheckRequest, + PriceResponse, + SpotTradeRequest, + TradeResponse, +} from "../types"; + +export class BinanceService { + private client: Spot; + + constructor(config?: BinanceConfig) { + this.client = new Spot(config?.apiKey, config?.secretKey, { + baseURL: config?.baseURL, + }); + } + + /** + * Get current price for a symbol + */ + async getPrice(request: PriceCheckRequest): Promise { + try { + const symbol = `${request.symbol}${request.quoteCurrency}`; + const response = await this.client.tickerPrice(symbol); + + return { + symbol, + price: response.data.price, + timestamp: Date.now(), + }; + } catch (error) { + throw new BinanceError( + `Failed to fetch price for ${request.symbol}`, + error.code, + error + ); + } + } + + /** + * Execute a spot trade + */ + async executeTrade(request: SpotTradeRequest): Promise { + let minNotional: string | undefined; + try { + // Log the incoming request + elizaLogger.log( + "Executing trade with request:", + JSON.stringify(request, null, 2) + ); + + // Always check if the symbol is valid first + const exchangeInfo = await this.client.exchangeInfo(); + const symbolInfo = exchangeInfo.data.symbols.find( + (s: any) => s.symbol === request.symbol + ); + + if (!symbolInfo) { + throw new Error( + `Trading pair ${request.symbol} is not available` + ); + } + + // Get minimum notional value from symbol filters + minNotional = symbolInfo.filters.find( + (f: any) => f.filterType === "NOTIONAL" + )?.minNotional; + + if (minNotional) { + elizaLogger.log( + `Minimum notional value for ${request.symbol}: ${minNotional} USDC` + ); + } + + const orderParams = this.buildOrderParams(request); + + // Log the final parameters being sent to Binance + elizaLogger.log( + "Sending order to Binance with params:", + JSON.stringify(orderParams, null, 2) + ); + + const response = await this.client.newOrder( + orderParams.symbol, + orderParams.side, + orderParams.type, + orderParams + ); + + // Log the response + elizaLogger.log( + "Received response from Binance:", + JSON.stringify(response.data, null, 2) + ); + + return { + symbol: response.data.symbol, + orderId: response.data.orderId, + status: response.data.status, + executedQty: response.data.executedQty, + cummulativeQuoteQty: response.data.cummulativeQuoteQty, + price: response.data.price, + type: response.data.type, + side: response.data.side, + }; + } catch (error) { + // Log the error details + elizaLogger.error("Error details:", { + message: error.message, + response: error.response?.data, + status: error.response?.status, + config: error.config, + }); + + if (error.response?.status === 401) { + throw new Error( + "Invalid API credentials. Please check your API key and secret." + ); + } + if ( + error.response?.data?.code === -1013 && + error.response?.data?.msg?.includes("NOTIONAL") + ) { + const minNotionalMsg = minNotional + ? ` Minimum order value is ${minNotional} USDC.` + : ""; + throw new Error( + `Order value is too small. Please increase the quantity to meet the minimum order value requirement.${minNotionalMsg}` + ); + } + throw new Error(error.response?.data?.msg || error.message); + } + } + + private buildOrderParams( + request: SpotTradeRequest + ): SpotTradeRequest & Record { + const params: SpotTradeRequest & Record = { + ...request, + symbol: request.symbol.toUpperCase(), + }; + + if (request.type === "LIMIT") { + if (!request.price) { + throw new BinanceError("Price is required for LIMIT orders"); + } + params.timeInForce = request.timeInForce || "GTC"; + } + + return params; + } + + /** + * Format price for display + */ + static formatPrice(price: number | string): string { + const numPrice = typeof price === "string" ? parseFloat(price) : price; + return new Intl.NumberFormat("en-US", { + style: "decimal", + minimumFractionDigits: 2, + maximumFractionDigits: 8, + }).format(numPrice); + } + + /** + * Get account balance for all assets or a specific asset + */ + async getBalance(request: BalanceCheckRequest): Promise { + try { + const response = await this.client.account(); + let balances = response.data.balances.filter( + (balance: { free: string; locked: string }) => + parseFloat(balance.free) > 0 || + parseFloat(balance.locked) > 0 + ); + + if (request.asset) { + balances = balances.filter( + (b: { asset: string }) => + b.asset.toUpperCase() === request.asset.toUpperCase() + ); + } + + return { + balances, + timestamp: Date.now(), + }; + } catch (error) { + if (error.response?.status === 401) { + throw new BinanceError("Invalid API credentials", 401); + } + throw new BinanceError( + request.asset + ? `Failed to fetch balance for ${request.asset}` + : "Failed to fetch account balances", + error.response?.status || error.code + ); + } + } +} diff --git a/packages/plugin-binance/src/types.ts b/packages/plugin-binance/src/types.ts new file mode 100644 index 0000000000..659b21aa57 --- /dev/null +++ b/packages/plugin-binance/src/types.ts @@ -0,0 +1,85 @@ +// types.ts +import { z } from "zod"; + +// Base configuration types +export interface BinanceConfig { + apiKey?: string; + secretKey?: string; + baseURL?: string; +} + +// Enhanced schemas with better validation +export const PriceCheckSchema = z.object({ + symbol: z.string().min(1).toUpperCase(), + quoteCurrency: z.string().min(1).toUpperCase().default("USDT"), +}); + +export const SpotTradeSchema = z.object({ + symbol: z.string().min(1).toUpperCase(), + side: z.enum(["BUY", "SELL"]), + type: z.enum(["MARKET", "LIMIT"]), + quantity: z.number().positive(), + price: z.number().positive().optional(), + timeInForce: z.enum(["GTC", "IOC", "FOK"]).optional().default("GTC"), +}); + +// Inferred types from schemas +export type PriceCheckRequest = z.infer; +export type SpotTradeRequest = z.infer; + +// Response types +export interface PriceResponse { + symbol: string; + price: string; + timestamp: number; +} + +export interface TradeResponse { + symbol: string; + orderId: number; + status: "NEW" | "PARTIALLY_FILLED" | "FILLED" | "CANCELED" | "REJECTED"; + executedQty: string; + cummulativeQuoteQty: string; + price: string; + type: SpotTradeRequest["type"]; + side: SpotTradeRequest["side"]; +} + +// Error handling types +export class BinanceError extends Error { + constructor( + message: string, + public code?: number, + public details?: unknown + ) { + super(message); + this.name = "BinanceError"; + } +} + +// Constants +export const TRADE_STATUS = { + NEW: "NEW", + PARTIALLY_FILLED: "PARTIALLY_FILLED", + FILLED: "FILLED", + CANCELED: "CANCELED", + REJECTED: "REJECTED", +} as const; + +export type TradeStatus = keyof typeof TRADE_STATUS; + +// Balance types +export interface BalanceCheckRequest { + asset?: string; +} + +export interface AssetBalance { + asset: string; + free: string; + locked: string; +} + +export interface BalanceResponse { + balances: AssetBalance[]; + timestamp: number; +} diff --git a/packages/plugin-binance/tsconfig.json b/packages/plugin-binance/tsconfig.json new file mode 100644 index 0000000000..834c4dce26 --- /dev/null +++ b/packages/plugin-binance/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/plugin-binance/tsup.config.ts b/packages/plugin-binance/tsup.config.ts new file mode 100644 index 0000000000..5cb9389e71 --- /dev/null +++ b/packages/plugin-binance/tsup.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], + dts: true, + splitting: false, + bundle: true, + minify: false, + external: [ + "@binance/connector", + "events", + "crypto", + "buffer", + "url", + "querystring", + "os", + ], + platform: "node", + target: "node18", +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90f147c715..aabddce7c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,6 +153,9 @@ importers: '@elizaos/plugin-avalanche': specifier: workspace:* version: link:../packages/plugin-avalanche + '@elizaos/plugin-binance': + specifier: workspace:* + version: link:../packages/plugin-binance '@elizaos/plugin-bootstrap': specifier: workspace:* version: link:../packages/plugin-bootstrap @@ -368,6 +371,9 @@ importers: '@docusaurus/preset-classic': specifier: 3.6.3 version: 3.6.3(@algolia/client-search@5.18.0)(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/react@18.3.12)(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@docusaurus/theme-common': + specifier: 3.6.3 + version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) '@docusaurus/theme-mermaid': specifier: 3.6.3 version: 3.6.3(@docusaurus/plugin-content-docs@3.6.3(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10))(@mdx-js/react@3.0.1(@types/react@18.3.12)(react@18.3.1))(@swc/core@1.10.4(@swc/helpers@0.5.15))(acorn@8.14.0)(bufferutil@4.0.9)(eslint@9.16.0(jiti@2.4.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)(utf-8-validate@5.0.10) @@ -383,6 +389,9 @@ importers: dotenv: specifier: ^16.4.7 version: 16.4.7 + lunr: + specifier: 2.3.9 + version: 2.3.9 prism-react-renderer: specifier: 2.3.1 version: 2.3.1(react@18.3.1) @@ -1053,6 +1062,25 @@ importers: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-binance: + dependencies: + '@binance/connector': + specifier: ^3.6.0 + version: 3.6.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@elizaos/core': + specifier: workspace:* + version: link:../core + zod: + specifier: ^3.22.4 + version: 3.23.8 + devDependencies: + '@types/node': + specifier: ^20.0.0 + version: 20.17.9 + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-bootstrap: dependencies: '@elizaos/core': @@ -1156,25 +1184,6 @@ importers: specifier: 7.1.0 version: 7.1.0 - packages/plugin-ferePro: - dependencies: - '@elizaos/core': - specifier: ^0.1.7-alpha.1 - version: 0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1) - tsup: - specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) - ws: - specifier: ^8.18.0 - version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - devDependencies: - '@types/ws': - specifier: ^8.5.13 - version: 8.5.13 - tsx: - specifier: ^4.19.2 - version: 4.19.2 - packages/plugin-flow: dependencies: '@elizaos/core': @@ -3133,6 +3142,10 @@ packages: bs58: ^6.0.0 viem: ^2.21.0 + '@binance/connector@3.6.0': + resolution: {integrity: sha512-n0MDi9Tm7N6mzgL/pcCjAnPXhlZkYeyh/jt1QKXu3WDcXFCiSNk0udVRMfDfkMbLCtGO68B4/UgkYweTkS3iCg==} + engines: {node: '>=12.22.3', npm: '>=6.14.13'} + '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} @@ -3871,9 +3884,6 @@ packages: peerDependencies: onnxruntime-node: 1.20.1 - '@elizaos/core@0.1.7-alpha.2': - resolution: {integrity: sha512-gNvFw/Xnv4dlcfmmKxRa+baKq6en4TitAjUGvo8LgAUkSk156A0fffJ0lAsc1rX8zMB5NsIqdvMCbwKxDd54OQ==} - '@emnapi/core@1.3.1': resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} @@ -21500,6 +21510,15 @@ snapshots: bs58: 6.0.0 viem: 2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@binance/connector@3.6.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + axios: 1.7.9(debug@4.4.0) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@braintree/sanitize-url@7.1.1': {} '@cfworker/json-schema@4.0.3': {} @@ -23022,56 +23041,6 @@ snapshots: '@huggingface/jinja': 0.2.2 onnxruntime-node: 1.20.1 - '@elizaos/core@0.1.7-alpha.2(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)': - dependencies: - '@ai-sdk/anthropic': 0.0.56(zod@3.23.8) - '@ai-sdk/google': 0.0.55(zod@3.23.8) - '@ai-sdk/google-vertex': 0.0.43(@google-cloud/vertexai@1.9.2(encoding@0.1.13))(zod@3.23.8) - '@ai-sdk/groq': 0.0.3(zod@3.23.8) - '@ai-sdk/openai': 1.0.5(zod@3.23.8) - '@anthropic-ai/sdk': 0.30.1(encoding@0.1.13) - '@fal-ai/client': 1.2.0 - '@types/uuid': 10.0.0 - ai: 3.4.33(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.16.1))(svelte@5.16.1)(vue@3.5.13(typescript@5.6.3))(zod@3.23.8) - anthropic-vertex-ai: 1.0.2(encoding@0.1.13)(zod@3.23.8) - fastembed: 1.14.1 - fastestsmallesttextencoderdecoder: 1.0.22 - gaxios: 6.7.1(encoding@0.1.13) - glob: 11.0.0 - handlebars: 4.7.8 - js-sha1: 0.7.0 - js-tiktoken: 1.0.15 - langchain: 0.3.6(@langchain/core@0.3.27(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.9)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)) - ollama-ai-provider: 0.16.1(zod@3.23.8) - openai: 4.73.0(encoding@0.1.13)(zod@3.23.8) - tinyld: 1.3.4 - together-ai: 0.7.0(encoding@0.1.13) - unique-names-generator: 4.7.1 - uuid: 11.0.3 - zod: 3.23.8 - transitivePeerDependencies: - - '@google-cloud/vertexai' - - '@langchain/anthropic' - - '@langchain/aws' - - '@langchain/cohere' - - '@langchain/core' - - '@langchain/google-genai' - - '@langchain/google-vertexai' - - '@langchain/groq' - - '@langchain/mistralai' - - '@langchain/ollama' - - axios - - cheerio - - encoding - - peggy - - react - - solid-js - - sswr - - supports-color - - svelte - - typeorm - - vue - '@emnapi/core@1.3.1': dependencies: '@emnapi/wasi-threads': 1.0.1 @@ -32900,6 +32869,7 @@ snapshots: '@esbuild/win32-arm64': 0.23.1 '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 + optional: true esbuild@0.24.2: optionalDependencies: @@ -33979,6 +33949,7 @@ snapshots: get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 + optional: true get-uri@6.0.4: dependencies: @@ -40553,7 +40524,8 @@ snapshots: resolve-pathname@3.0.0: {} - resolve-pkg-maps@1.0.0: {} + resolve-pkg-maps@1.0.0: + optional: true resolve.exports@2.0.3: {} @@ -42176,6 +42148,7 @@ snapshots: get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 + optional: true tty-browserify@0.0.1: {} From bee0a3c679eb1bb006da4ff8d370440745b88ce8 Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Sat, 4 Jan 2025 13:58:36 +0100 Subject: [PATCH 24/58] fix: clean elizalogger --- .../plugin-binance/src/actions/priceCheck.ts | 2 -- .../plugin-binance/src/actions/spotBalance.ts | 2 -- .../plugin-binance/src/actions/spotTrade.ts | 16 +------------ .../src/services/BinanceService.ts | 24 ------------------- 4 files changed, 1 insertion(+), 43 deletions(-) diff --git a/packages/plugin-binance/src/actions/priceCheck.ts b/packages/plugin-binance/src/actions/priceCheck.ts index d118140ab4..60fd054ae5 100644 --- a/packages/plugin-binance/src/actions/priceCheck.ts +++ b/packages/plugin-binance/src/actions/priceCheck.ts @@ -50,8 +50,6 @@ export const priceCheck: Action = { _options: Record, callback?: HandlerCallback ): Promise => { - elizaLogger.log("Starting Binance GET_PRICE handler..."); - try { // Initialize or update state state = !state diff --git a/packages/plugin-binance/src/actions/spotBalance.ts b/packages/plugin-binance/src/actions/spotBalance.ts index af0e755f82..a231379cbb 100644 --- a/packages/plugin-binance/src/actions/spotBalance.ts +++ b/packages/plugin-binance/src/actions/spotBalance.ts @@ -58,8 +58,6 @@ export const spotBalance: Action = { _options: { [key: string]: unknown }, callback?: HandlerCallback ): Promise => { - elizaLogger.log("Starting Binance GET_SPOT_BALANCE handler..."); - if (!state) { state = (await runtime.composeState(message)) as State; } else { diff --git a/packages/plugin-binance/src/actions/spotTrade.ts b/packages/plugin-binance/src/actions/spotTrade.ts index e763385d07..4931a95ebc 100644 --- a/packages/plugin-binance/src/actions/spotTrade.ts +++ b/packages/plugin-binance/src/actions/spotTrade.ts @@ -57,8 +57,6 @@ export const spotTrade: Action = { _options: Record, callback?: HandlerCallback ): Promise => { - elizaLogger.log("Starting EXECUTE_SPOT_TRADE handler..."); - let content; try { state = !state @@ -76,21 +74,11 @@ export const spotTrade: Action = { modelClass: ModelClass.SMALL, }); - elizaLogger.log("Generated content:", content); - // Convert quantity to number if it's a string if (content && typeof content.quantity === "string") { content.quantity = parseFloat(content.quantity); } - elizaLogger.log("Attempting to parse content:", { - originalContent: content, - quantity: content?.quantity, - quantityType: content?.quantity - ? typeof content.quantity - : "undefined", - }); - const parseResult = SpotTradeSchema.safeParse(content); if (!parseResult.success) { throw new Error( @@ -120,11 +108,9 @@ export const spotTrade: Action = { return true; } catch (error) { elizaLogger.error("Error executing trade:", { - error, content, message: error.message, - name: error.name, - stack: error.stack, + code: error.code, }); if (callback) { callback({ diff --git a/packages/plugin-binance/src/services/BinanceService.ts b/packages/plugin-binance/src/services/BinanceService.ts index 1fda55544c..e185f55103 100644 --- a/packages/plugin-binance/src/services/BinanceService.ts +++ b/packages/plugin-binance/src/services/BinanceService.ts @@ -49,12 +49,6 @@ export class BinanceService { async executeTrade(request: SpotTradeRequest): Promise { let minNotional: string | undefined; try { - // Log the incoming request - elizaLogger.log( - "Executing trade with request:", - JSON.stringify(request, null, 2) - ); - // Always check if the symbol is valid first const exchangeInfo = await this.client.exchangeInfo(); const symbolInfo = exchangeInfo.data.symbols.find( @@ -72,20 +66,8 @@ export class BinanceService { (f: any) => f.filterType === "NOTIONAL" )?.minNotional; - if (minNotional) { - elizaLogger.log( - `Minimum notional value for ${request.symbol}: ${minNotional} USDC` - ); - } - const orderParams = this.buildOrderParams(request); - // Log the final parameters being sent to Binance - elizaLogger.log( - "Sending order to Binance with params:", - JSON.stringify(orderParams, null, 2) - ); - const response = await this.client.newOrder( orderParams.symbol, orderParams.side, @@ -93,12 +75,6 @@ export class BinanceService { orderParams ); - // Log the response - elizaLogger.log( - "Received response from Binance:", - JSON.stringify(response.data, null, 2) - ); - return { symbol: response.data.symbol, orderId: response.data.orderId, From f5f469f8e3476cadf617c2639448c3aaab77b865 Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Sat, 4 Jan 2025 14:07:59 +0100 Subject: [PATCH 25/58] fix: better code organization --- packages/plugin-binance/src/constants/api.ts | 31 +++ .../plugin-binance/src/constants/defaults.ts | 22 +++ .../plugin-binance/src/constants/errors.ts | 33 ++++ packages/plugin-binance/src/index.ts | 5 + .../src/services/BinanceService.ts | 182 ------------------ .../plugin-binance/src/services/account.ts | 93 +++++++++ packages/plugin-binance/src/services/base.ts | 95 +++++++++ packages/plugin-binance/src/services/index.ts | 52 +++++ packages/plugin-binance/src/services/price.ts | 57 ++++++ packages/plugin-binance/src/services/trade.ts | 114 +++++++++++ .../plugin-binance/src/types/api/account.ts | 79 ++++++++ .../plugin-binance/src/types/api/price.ts | 81 ++++++++ .../plugin-binance/src/types/api/trade.ts | 81 ++++++++ packages/plugin-binance/src/types/index.ts | 8 + .../src/types/internal/config.ts | 79 ++++++++ .../src/types/internal/error.ts | 105 ++++++++++ 16 files changed, 935 insertions(+), 182 deletions(-) create mode 100644 packages/plugin-binance/src/constants/api.ts create mode 100644 packages/plugin-binance/src/constants/defaults.ts create mode 100644 packages/plugin-binance/src/constants/errors.ts delete mode 100644 packages/plugin-binance/src/services/BinanceService.ts create mode 100644 packages/plugin-binance/src/services/account.ts create mode 100644 packages/plugin-binance/src/services/base.ts create mode 100644 packages/plugin-binance/src/services/index.ts create mode 100644 packages/plugin-binance/src/services/price.ts create mode 100644 packages/plugin-binance/src/services/trade.ts create mode 100644 packages/plugin-binance/src/types/api/account.ts create mode 100644 packages/plugin-binance/src/types/api/price.ts create mode 100644 packages/plugin-binance/src/types/api/trade.ts create mode 100644 packages/plugin-binance/src/types/index.ts create mode 100644 packages/plugin-binance/src/types/internal/config.ts create mode 100644 packages/plugin-binance/src/types/internal/error.ts diff --git a/packages/plugin-binance/src/constants/api.ts b/packages/plugin-binance/src/constants/api.ts new file mode 100644 index 0000000000..b1d6217923 --- /dev/null +++ b/packages/plugin-binance/src/constants/api.ts @@ -0,0 +1,31 @@ +export const API_DEFAULTS = { + BASE_URL: "https://api.binance.com", + TIMEOUT: 30000, // 30 seconds + RATE_LIMIT: { + MAX_REQUESTS_PER_MINUTE: 1200, + WEIGHT_PER_REQUEST: 1, + }, +}; + +export const API_ENDPOINTS = { + TICKER: "/api/v3/ticker/price", + ACCOUNT: "/api/v3/account", + ORDER: "/api/v3/order", + EXCHANGE_INFO: "/api/v3/exchangeInfo", +}; + +export const ORDER_TYPES = { + MARKET: "MARKET", + LIMIT: "LIMIT", +} as const; + +export const ORDER_SIDES = { + BUY: "BUY", + SELL: "SELL", +} as const; + +export const TIME_IN_FORCE = { + GTC: "GTC", // Good Till Cancel + IOC: "IOC", // Immediate or Cancel + FOK: "FOK", // Fill or Kill +} as const; diff --git a/packages/plugin-binance/src/constants/defaults.ts b/packages/plugin-binance/src/constants/defaults.ts new file mode 100644 index 0000000000..ff34231098 --- /dev/null +++ b/packages/plugin-binance/src/constants/defaults.ts @@ -0,0 +1,22 @@ +export const TRADE_DEFAULTS = { + QUOTE_CURRENCY: "USDT", + TIME_IN_FORCE: "GTC", + ORDER_TYPE: "MARKET", + PRICE_PRECISION: 8, + QUANTITY_PRECISION: 8, +}; + +export const DISPLAY_DEFAULTS = { + PRICE_FORMAT: { + MIN_FRACTION_DIGITS: 2, + MAX_FRACTION_DIGITS: 8, + LOCALE: "en-US", + }, +}; + +export const VALIDATION = { + SYMBOL: { + MIN_LENGTH: 2, + MAX_LENGTH: 10, + }, +}; diff --git a/packages/plugin-binance/src/constants/errors.ts b/packages/plugin-binance/src/constants/errors.ts new file mode 100644 index 0000000000..2558fe9c8f --- /dev/null +++ b/packages/plugin-binance/src/constants/errors.ts @@ -0,0 +1,33 @@ +export const ERROR_CODES = { + INVALID_CREDENTIALS: 401, + INVALID_PARAMETERS: 400, + INSUFFICIENT_BALANCE: -1012, + MIN_NOTIONAL_NOT_MET: -1013, + UNKNOWN_ORDER_COMPOSITION: -1111, + PRICE_QTY_EXCEED_HARD_LIMITS: -1021, +} as const; + +export const ERROR_MESSAGES = { + INVALID_CREDENTIALS: + "Invalid API credentials. Please check your API key and secret.", + INVALID_SYMBOL: "Invalid trading pair symbol", + SYMBOL_NOT_FOUND: (symbol: string) => + `Trading pair ${symbol} is not available`, + MIN_NOTIONAL_NOT_MET: (minNotional?: string) => + `Order value is too small. Please increase the quantity to meet the minimum order value requirement.${ + minNotional ? ` Minimum order value is ${minNotional} USDC.` : "" + }`, + LIMIT_ORDER_PRICE_REQUIRED: "Price is required for LIMIT orders", + BALANCE_FETCH_ERROR: (asset?: string) => + asset + ? `Failed to fetch balance for ${asset}` + : "Failed to fetch account balances", + PRICE_FETCH_ERROR: (symbol: string) => + `Failed to fetch price for ${symbol}`, +} as const; + +export const VALIDATION_ERRORS = { + MISSING_API_KEY: "BINANCE_API_KEY is required but not configured", + MISSING_SECRET_KEY: "BINANCE_SECRET_KEY is required but not configured", + INVALID_SYMBOL_LENGTH: "Invalid cryptocurrency symbol length", +} as const; diff --git a/packages/plugin-binance/src/index.ts b/packages/plugin-binance/src/index.ts index c8922831ee..79e0bebe80 100644 --- a/packages/plugin-binance/src/index.ts +++ b/packages/plugin-binance/src/index.ts @@ -3,6 +3,11 @@ import { priceCheck } from "./actions/priceCheck"; import { spotBalance } from "./actions/spotBalance"; import { spotTrade } from "./actions/spotTrade"; +// Re-export types and services for external use +export * from "./services"; +export * from "./types"; + +// Export the plugin configuration export const binancePlugin: Plugin = { name: "binance", description: "Binance Plugin for Eliza", diff --git a/packages/plugin-binance/src/services/BinanceService.ts b/packages/plugin-binance/src/services/BinanceService.ts deleted file mode 100644 index e185f55103..0000000000 --- a/packages/plugin-binance/src/services/BinanceService.ts +++ /dev/null @@ -1,182 +0,0 @@ -// services/BinanceService.ts -import { Spot } from "@binance/connector"; -import { elizaLogger } from "@elizaos/core"; -import { - BalanceCheckRequest, - BalanceResponse, - BinanceConfig, - BinanceError, - PriceCheckRequest, - PriceResponse, - SpotTradeRequest, - TradeResponse, -} from "../types"; - -export class BinanceService { - private client: Spot; - - constructor(config?: BinanceConfig) { - this.client = new Spot(config?.apiKey, config?.secretKey, { - baseURL: config?.baseURL, - }); - } - - /** - * Get current price for a symbol - */ - async getPrice(request: PriceCheckRequest): Promise { - try { - const symbol = `${request.symbol}${request.quoteCurrency}`; - const response = await this.client.tickerPrice(symbol); - - return { - symbol, - price: response.data.price, - timestamp: Date.now(), - }; - } catch (error) { - throw new BinanceError( - `Failed to fetch price for ${request.symbol}`, - error.code, - error - ); - } - } - - /** - * Execute a spot trade - */ - async executeTrade(request: SpotTradeRequest): Promise { - let minNotional: string | undefined; - try { - // Always check if the symbol is valid first - const exchangeInfo = await this.client.exchangeInfo(); - const symbolInfo = exchangeInfo.data.symbols.find( - (s: any) => s.symbol === request.symbol - ); - - if (!symbolInfo) { - throw new Error( - `Trading pair ${request.symbol} is not available` - ); - } - - // Get minimum notional value from symbol filters - minNotional = symbolInfo.filters.find( - (f: any) => f.filterType === "NOTIONAL" - )?.minNotional; - - const orderParams = this.buildOrderParams(request); - - const response = await this.client.newOrder( - orderParams.symbol, - orderParams.side, - orderParams.type, - orderParams - ); - - return { - symbol: response.data.symbol, - orderId: response.data.orderId, - status: response.data.status, - executedQty: response.data.executedQty, - cummulativeQuoteQty: response.data.cummulativeQuoteQty, - price: response.data.price, - type: response.data.type, - side: response.data.side, - }; - } catch (error) { - // Log the error details - elizaLogger.error("Error details:", { - message: error.message, - response: error.response?.data, - status: error.response?.status, - config: error.config, - }); - - if (error.response?.status === 401) { - throw new Error( - "Invalid API credentials. Please check your API key and secret." - ); - } - if ( - error.response?.data?.code === -1013 && - error.response?.data?.msg?.includes("NOTIONAL") - ) { - const minNotionalMsg = minNotional - ? ` Minimum order value is ${minNotional} USDC.` - : ""; - throw new Error( - `Order value is too small. Please increase the quantity to meet the minimum order value requirement.${minNotionalMsg}` - ); - } - throw new Error(error.response?.data?.msg || error.message); - } - } - - private buildOrderParams( - request: SpotTradeRequest - ): SpotTradeRequest & Record { - const params: SpotTradeRequest & Record = { - ...request, - symbol: request.symbol.toUpperCase(), - }; - - if (request.type === "LIMIT") { - if (!request.price) { - throw new BinanceError("Price is required for LIMIT orders"); - } - params.timeInForce = request.timeInForce || "GTC"; - } - - return params; - } - - /** - * Format price for display - */ - static formatPrice(price: number | string): string { - const numPrice = typeof price === "string" ? parseFloat(price) : price; - return new Intl.NumberFormat("en-US", { - style: "decimal", - minimumFractionDigits: 2, - maximumFractionDigits: 8, - }).format(numPrice); - } - - /** - * Get account balance for all assets or a specific asset - */ - async getBalance(request: BalanceCheckRequest): Promise { - try { - const response = await this.client.account(); - let balances = response.data.balances.filter( - (balance: { free: string; locked: string }) => - parseFloat(balance.free) > 0 || - parseFloat(balance.locked) > 0 - ); - - if (request.asset) { - balances = balances.filter( - (b: { asset: string }) => - b.asset.toUpperCase() === request.asset.toUpperCase() - ); - } - - return { - balances, - timestamp: Date.now(), - }; - } catch (error) { - if (error.response?.status === 401) { - throw new BinanceError("Invalid API credentials", 401); - } - throw new BinanceError( - request.asset - ? `Failed to fetch balance for ${request.asset}` - : "Failed to fetch account balances", - error.response?.status || error.code - ); - } - } -} diff --git a/packages/plugin-binance/src/services/account.ts b/packages/plugin-binance/src/services/account.ts new file mode 100644 index 0000000000..2bf5159073 --- /dev/null +++ b/packages/plugin-binance/src/services/account.ts @@ -0,0 +1,93 @@ +import { BinanceAccountInfo, BinanceBalance } from "../types/api/account"; +import { BalanceCheckRequest, BalanceResponse } from "../types/internal/config"; +import { BaseService } from "./base"; + +/** + * Service for handling account-related operations + */ +export class AccountService extends BaseService { + /** + * Get account balance for all assets or a specific asset + */ + async getBalance(request: BalanceCheckRequest): Promise { + try { + this.validateCredentials(); + + const response = await this.client.account(); + const accountInfo = response.data as BinanceAccountInfo; + + let balances = this.filterNonZeroBalances(accountInfo.balances); + + if (request.asset) { + balances = this.filterByAsset(balances, request.asset); + } + + return { + balances, + timestamp: Date.now(), + }; + } catch (error) { + throw this.handleError( + error, + request.asset ? `Asset: ${request.asset}` : "All assets" + ); + } + } + + /** + * Filter out zero balances + */ + private filterNonZeroBalances( + balances: BinanceBalance[] + ): BinanceBalance[] { + return balances.filter( + (balance) => + parseFloat(balance.free) > 0 || parseFloat(balance.locked) > 0 + ); + } + + /** + * Filter balances by asset + */ + private filterByAsset( + balances: BinanceBalance[], + asset: string + ): BinanceBalance[] { + return balances.filter( + (b) => b.asset.toUpperCase() === asset.toUpperCase() + ); + } + + /** + * Get account trading status + */ + async getTradingStatus(): Promise { + try { + this.validateCredentials(); + const response = await this.client.account(); + const accountInfo = response.data as BinanceAccountInfo; + return accountInfo.canTrade; + } catch (error) { + throw this.handleError(error, "Trading status check"); + } + } + + /** + * Check if account has sufficient balance for a trade + */ + async checkBalance(asset: string, required: number): Promise { + try { + const { balances } = await this.getBalance({ asset }); + const balance = balances[0]; + + if (!balance) { + return false; + } + + const available = parseFloat(balance.free); + return available >= required; + } catch (error) { + throw this.handleError(error, `Balance check for ${asset}`); + } + } +} diff --git a/packages/plugin-binance/src/services/base.ts b/packages/plugin-binance/src/services/base.ts new file mode 100644 index 0000000000..37bdb9ac75 --- /dev/null +++ b/packages/plugin-binance/src/services/base.ts @@ -0,0 +1,95 @@ +import { Spot } from "@binance/connector"; +import { elizaLogger } from "@elizaos/core"; +import { API_DEFAULTS } from "../constants/api"; +import { ERROR_MESSAGES } from "../constants/errors"; +import { BinanceConfig, ServiceOptions } from "../types/internal/config"; +import { + ApiError, + AuthenticationError, + BinanceError, + InvalidSymbolError, + MinNotionalError, +} from "../types/internal/error"; + +/** + * Base service class with common functionality + */ +export abstract class BaseService { + protected client: Spot; + protected config: BinanceConfig; + + constructor(config?: BinanceConfig) { + this.config = { + baseURL: API_DEFAULTS.BASE_URL, + timeout: API_DEFAULTS.TIMEOUT, + ...config, + }; + + this.client = new Spot(this.config.apiKey, this.config.secretKey, { + baseURL: this.config.baseURL, + timeout: this.config.timeout, + }); + } + + /** + * Handles common error scenarios and transforms them into appropriate error types + */ + protected handleError(error: unknown, context?: string): never { + if (error instanceof BinanceError) { + throw error; + } + + const apiError = error as any; + const errorResponse = apiError.response?.data; + const errorCode = errorResponse?.code || apiError.code; + const errorMessage = errorResponse?.msg || apiError.message; + + // Handle authentication errors + if (apiError.response?.status === 401) { + throw new AuthenticationError(ERROR_MESSAGES.INVALID_CREDENTIALS); + } + + // Handle minimum notional errors + if (errorCode === -1013 && errorMessage?.includes("NOTIONAL")) { + throw new MinNotionalError(); + } + + // Handle invalid symbol errors + if (errorMessage?.includes("Invalid symbol")) { + throw new InvalidSymbolError(context || "Unknown"); + } + + // Log unexpected errors for debugging + elizaLogger.error("Unexpected API error:", { + context, + code: errorCode, + message: errorMessage, + response: errorResponse, + }); + + throw new ApiError( + errorMessage || "An unexpected error occurred", + errorCode || 500, + errorResponse + ); + } + + /** + * Validates required API credentials + */ + protected validateCredentials(): void { + if (!this.config.apiKey || !this.config.secretKey) { + throw new AuthenticationError("API credentials are required"); + } + } + + /** + * Merges default options with provided options + */ + protected mergeOptions(options?: ServiceOptions): ServiceOptions { + return { + timeout: this.config.timeout, + ...options, + }; + } +} diff --git a/packages/plugin-binance/src/services/index.ts b/packages/plugin-binance/src/services/index.ts new file mode 100644 index 0000000000..cf22c90825 --- /dev/null +++ b/packages/plugin-binance/src/services/index.ts @@ -0,0 +1,52 @@ +import { BinanceConfig } from "../types/internal/config"; +import { AccountService } from "./account"; +import { PriceService } from "./price"; +import { TradeService } from "./trade"; + +/** + * Main service facade that coordinates between specialized services + */ +export class BinanceService { + private priceService: PriceService; + private tradeService: TradeService; + private accountService: AccountService; + + constructor(config?: BinanceConfig) { + this.priceService = new PriceService(config); + this.tradeService = new TradeService(config); + this.accountService = new AccountService(config); + } + + /** + * Price-related operations + */ + async getPrice(...args: Parameters) { + return this.priceService.getPrice(...args); + } + + static formatPrice = PriceService.formatPrice; + + /** + * Trading operations + */ + async executeTrade(...args: Parameters) { + return this.tradeService.executeTrade(...args); + } + + /** + * Account operations + */ + async getBalance(...args: Parameters) { + return this.accountService.getBalance(...args); + } + + async getTradingStatus() { + return this.accountService.getTradingStatus(); + } + + async checkBalance(...args: Parameters) { + return this.accountService.checkBalance(...args); + } +} + +export { AccountService, PriceService, TradeService }; diff --git a/packages/plugin-binance/src/services/price.ts b/packages/plugin-binance/src/services/price.ts new file mode 100644 index 0000000000..f549c88300 --- /dev/null +++ b/packages/plugin-binance/src/services/price.ts @@ -0,0 +1,57 @@ +import { VALIDATION } from "../constants/defaults"; +import { ERROR_MESSAGES } from "../constants/errors"; +import { BinanceTickerResponse } from "../types/api/price"; +import { PriceCheckRequest, PriceResponse } from "../types/internal/config"; +import { BinanceError } from "../types/internal/error"; +import { BaseService } from "./base"; + +/** + * Service for handling price-related operations + */ +export class PriceService extends BaseService { + /** + * Get current price for a symbol + */ + async getPrice(request: PriceCheckRequest): Promise { + try { + this.validateSymbol(request.symbol); + + const symbol = `${request.symbol}${request.quoteCurrency}`; + const response = await this.client.tickerPrice(symbol); + const data = response.data as BinanceTickerResponse; + + return { + symbol, + price: data.price, + timestamp: Date.now(), + }; + } catch (error) { + throw this.handleError(error, request.symbol); + } + } + + /** + * Validates symbol format + */ + private validateSymbol(symbol: string): void { + const trimmedSymbol = symbol.trim(); + if ( + trimmedSymbol.length < VALIDATION.SYMBOL.MIN_LENGTH || + trimmedSymbol.length > VALIDATION.SYMBOL.MAX_LENGTH + ) { + throw new BinanceError(ERROR_MESSAGES.INVALID_SYMBOL); + } + } + + /** + * Format price for display + */ + static formatPrice(price: number | string): string { + const numPrice = typeof price === "string" ? parseFloat(price) : price; + return new Intl.NumberFormat("en-US", { + style: "decimal", + minimumFractionDigits: 2, + maximumFractionDigits: 8, + }).format(numPrice); + } +} diff --git a/packages/plugin-binance/src/services/trade.ts b/packages/plugin-binance/src/services/trade.ts new file mode 100644 index 0000000000..24ac5e0f51 --- /dev/null +++ b/packages/plugin-binance/src/services/trade.ts @@ -0,0 +1,114 @@ +import { ORDER_TYPES, TIME_IN_FORCE } from "../constants/api"; +import { ERROR_MESSAGES } from "../constants/errors"; +import { + BinanceExchangeInfo, + BinanceSymbolFilter, + BinanceSymbolInfo, +} from "../types/api/price"; +import { + BinanceNewOrderParams, + BinanceOrderResponse, +} from "../types/api/trade"; +import { SpotTradeRequest, TradeResponse } from "../types/internal/config"; +import { InvalidSymbolError, MinNotionalError } from "../types/internal/error"; +import { BaseService } from "./base"; + +/** + * Service for handling trading operations + */ +export class TradeService extends BaseService { + /** + * Execute a spot trade + */ + async executeTrade(request: SpotTradeRequest): Promise { + try { + this.validateCredentials(); + await this.validateSymbol(request.symbol); + + const orderParams = this.buildOrderParams(request); + const response = await this.client.newOrder( + orderParams.symbol, + orderParams.side, + orderParams.type, + orderParams + ); + + const data = response.data as BinanceOrderResponse; + return { + symbol: data.symbol, + orderId: data.orderId, + status: data.status, + executedQty: data.executedQty, + cummulativeQuoteQty: data.cummulativeQuoteQty, + price: data.price, + type: data.type, + side: data.side, + }; + } catch (error) { + throw this.handleError(error, request.symbol); + } + } + + /** + * Validate trading pair and get symbol information + */ + private async validateSymbol(symbol: string): Promise { + const exchangeInfo = await this.client.exchangeInfo(); + const data = exchangeInfo.data as BinanceExchangeInfo; + + const symbolInfo = data.symbols.find((s) => s.symbol === symbol); + if (!symbolInfo) { + throw new InvalidSymbolError(symbol); + } + + return symbolInfo; + } + + /** + * Build order parameters for the Binance API + */ + private buildOrderParams(request: SpotTradeRequest): BinanceNewOrderParams { + const params: BinanceNewOrderParams = { + symbol: request.symbol.toUpperCase(), + side: request.side, + type: request.type, + quantity: request.quantity.toString(), + }; + + if (request.type === ORDER_TYPES.LIMIT) { + if (!request.price) { + throw new Error(ERROR_MESSAGES.LIMIT_ORDER_PRICE_REQUIRED); + } + params.timeInForce = request.timeInForce || TIME_IN_FORCE.GTC; + params.price = request.price.toString(); + } + + return params; + } + + /** + * Get minimum notional value from symbol filters + */ + private getMinNotional(filters: BinanceSymbolFilter[]): string | undefined { + const notionalFilter = filters.find((f) => f.filterType === "NOTIONAL"); + return notionalFilter?.minNotional; + } + + /** + * Check if order meets minimum notional value + */ + private checkMinNotional( + symbolInfo: BinanceSymbolInfo, + quantity: number, + price?: number + ): void { + const minNotional = this.getMinNotional(symbolInfo.filters); + if (!minNotional) return; + + const notionalValue = price ? quantity * price : quantity; // For market orders, quantity is in quote currency + + if (parseFloat(minNotional) > notionalValue) { + throw new MinNotionalError(minNotional); + } + } +} diff --git a/packages/plugin-binance/src/types/api/account.ts b/packages/plugin-binance/src/types/api/account.ts new file mode 100644 index 0000000000..1f9e88655c --- /dev/null +++ b/packages/plugin-binance/src/types/api/account.ts @@ -0,0 +1,79 @@ +/** + * Binance API account information response + */ +export interface BinanceAccountInfo { + makerCommission: number; + takerCommission: number; + buyerCommission: number; + sellerCommission: number; + canTrade: boolean; + canWithdraw: boolean; + canDeposit: boolean; + updateTime: number; + accountType: string; + balances: BinanceBalance[]; + permissions: string[]; +} + +/** + * Balance information for a single asset + */ +export interface BinanceBalance { + asset: string; + free: string; // Available balance + locked: string; // Locked in orders +} + +/** + * Account trade list response + */ +export interface BinanceAccountTrade { + symbol: string; + id: number; + orderId: number; + orderListId: number; + price: string; + qty: string; + quoteQty: string; + commission: string; + commissionAsset: string; + time: number; + isBuyer: boolean; + isMaker: boolean; + isBestMatch: boolean; +} + +/** + * Parameters for account trade list query + */ +export interface BinanceTradeListParams { + symbol: string; + orderId?: number; + startTime?: number; + endTime?: number; + fromId?: number; + limit?: number; +} + +/** + * Account status response + */ +export interface BinanceAccountStatus { + data: string; // "Normal", "Margin", "Futures", etc. +} + +/** + * API trading status response + */ +export interface BinanceApiTradingStatus { + data: { + isLocked: boolean; + plannedRecoverTime: number; + triggerCondition: { + gcr: number; + ifer: number; + ufr: number; + }; + updateTime: number; + }; +} diff --git a/packages/plugin-binance/src/types/api/price.ts b/packages/plugin-binance/src/types/api/price.ts new file mode 100644 index 0000000000..2990bdc2d2 --- /dev/null +++ b/packages/plugin-binance/src/types/api/price.ts @@ -0,0 +1,81 @@ +/** + * Binance API response for ticker price endpoint + */ +export interface BinanceTickerResponse { + symbol: string; + price: string; +} + +/** + * Binance API response for 24hr ticker + */ +export interface BinanceTickerStatistics { + symbol: string; + priceChange: string; + priceChangePercent: string; + weightedAvgPrice: string; + prevClosePrice: string; + lastPrice: string; + lastQty: string; + bidPrice: string; + bidQty: string; + askPrice: string; + askQty: string; + openPrice: string; + highPrice: string; + lowPrice: string; + volume: string; + quoteVolume: string; + openTime: number; + closeTime: number; + firstId: number; + lastId: number; + count: number; +} + +/** + * Exchange information for a symbol + */ +export interface BinanceSymbolInfo { + symbol: string; + status: string; + baseAsset: string; + baseAssetPrecision: number; + quoteAsset: string; + quotePrecision: number; + quoteAssetPrecision: number; + filters: BinanceSymbolFilter[]; +} + +/** + * Symbol filter types + */ +export interface BinanceSymbolFilter { + filterType: string; + minPrice?: string; + maxPrice?: string; + tickSize?: string; + minQty?: string; + maxQty?: string; + stepSize?: string; + minNotional?: string; + limit?: number; + multiplierUp?: string; + multiplierDown?: string; + avgPriceMins?: number; +} + +/** + * Exchange information response + */ +export interface BinanceExchangeInfo { + timezone: string; + serverTime: number; + rateLimits: Array<{ + rateLimitType: string; + interval: string; + intervalNum: number; + limit: number; + }>; + symbols: BinanceSymbolInfo[]; +} diff --git a/packages/plugin-binance/src/types/api/trade.ts b/packages/plugin-binance/src/types/api/trade.ts new file mode 100644 index 0000000000..5abb3fe43f --- /dev/null +++ b/packages/plugin-binance/src/types/api/trade.ts @@ -0,0 +1,81 @@ +import { ORDER_SIDES, ORDER_TYPES, TIME_IN_FORCE } from "../../constants/api"; + +export type OrderType = (typeof ORDER_TYPES)[keyof typeof ORDER_TYPES]; +export type OrderSide = (typeof ORDER_SIDES)[keyof typeof ORDER_SIDES]; +export type TimeInForce = (typeof TIME_IN_FORCE)[keyof typeof TIME_IN_FORCE]; + +/** + * Binance API new order response + */ +export interface BinanceOrderResponse { + symbol: string; + orderId: number; + orderListId: number; + clientOrderId: string; + transactTime: number; + price: string; + origQty: string; + executedQty: string; + cummulativeQuoteQty: string; + status: OrderStatus; + timeInForce: TimeInForce; + type: OrderType; + side: OrderSide; + fills?: OrderFill[]; +} + +/** + * Order fill information + */ +export interface OrderFill { + price: string; + qty: string; + commission: string; + commissionAsset: string; + tradeId: number; +} + +/** + * Order status types + */ +export type OrderStatus = + | "NEW" + | "PARTIALLY_FILLED" + | "FILLED" + | "CANCELED" + | "PENDING_CANCEL" + | "REJECTED" + | "EXPIRED"; + +/** + * New order parameters for Binance API + */ +export interface BinanceNewOrderParams { + symbol: string; + side: OrderSide; + type: OrderType; + timeInForce?: TimeInForce; + quantity?: string | number; + quoteOrderQty?: string | number; + price?: string | number; + newClientOrderId?: string; + stopPrice?: string | number; + icebergQty?: string | number; + newOrderRespType?: "ACK" | "RESULT" | "FULL"; +} + +/** + * Order query parameters + */ +export interface BinanceOrderQueryParams { + symbol: string; + orderId?: number; + origClientOrderId?: string; +} + +/** + * Cancel order parameters + */ +export interface BinanceCancelOrderParams extends BinanceOrderQueryParams { + newClientOrderId?: string; +} diff --git a/packages/plugin-binance/src/types/index.ts b/packages/plugin-binance/src/types/index.ts new file mode 100644 index 0000000000..8b2f12fbcb --- /dev/null +++ b/packages/plugin-binance/src/types/index.ts @@ -0,0 +1,8 @@ +// API Types +export * from "./api/account"; +export * from "./api/price"; +export * from "./api/trade"; + +// Internal Types +export * from "./internal/config"; +export * from "./internal/error"; diff --git a/packages/plugin-binance/src/types/internal/config.ts b/packages/plugin-binance/src/types/internal/config.ts new file mode 100644 index 0000000000..f1648c9c9a --- /dev/null +++ b/packages/plugin-binance/src/types/internal/config.ts @@ -0,0 +1,79 @@ +/** + * Binance service configuration + */ +export interface BinanceConfig { + apiKey?: string; + secretKey?: string; + baseURL?: string; + timeout?: number; +} + +/** + * Service options that can be passed to any service method + */ +export interface ServiceOptions { + timeout?: number; + recvWindow?: number; +} + +/** + * Price check request parameters + */ +export interface PriceCheckRequest { + symbol: string; + quoteCurrency: string; +} + +/** + * Price response data + */ +export interface PriceResponse { + symbol: string; + price: string; + timestamp: number; +} + +/** + * Spot trade request parameters + */ +export interface SpotTradeRequest { + symbol: string; + side: "BUY" | "SELL"; + type: "MARKET" | "LIMIT"; + quantity: number; + price?: number; + timeInForce?: "GTC" | "IOC" | "FOK"; +} + +/** + * Trade response data + */ +export interface TradeResponse { + symbol: string; + orderId: number; + status: string; + executedQty: string; + cummulativeQuoteQty: string; + price: string; + type: string; + side: string; +} + +/** + * Balance check request parameters + */ +export interface BalanceCheckRequest { + asset?: string; +} + +/** + * Balance response data + */ +export interface BalanceResponse { + balances: Array<{ + asset: string; + free: string; + locked: string; + }>; + timestamp: number; +} diff --git a/packages/plugin-binance/src/types/internal/error.ts b/packages/plugin-binance/src/types/internal/error.ts new file mode 100644 index 0000000000..1709172d1e --- /dev/null +++ b/packages/plugin-binance/src/types/internal/error.ts @@ -0,0 +1,105 @@ +import { ERROR_CODES } from "../../constants/errors"; + +type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES]; + +/** + * Base error class for Binance-related errors + */ +export class BinanceError extends Error { + public readonly code: ErrorCode | number; + public readonly originalError?: unknown; + + constructor( + message: string, + code: ErrorCode | number = ERROR_CODES.INVALID_PARAMETERS, + originalError?: unknown + ) { + super(message); + this.name = "BinanceError"; + this.code = code; + this.originalError = originalError; + + // Maintains proper stack trace for where error was thrown + if (Error.captureStackTrace) { + Error.captureStackTrace(this, BinanceError); + } + } +} + +/** + * Error thrown when API credentials are invalid or missing + */ +export class AuthenticationError extends BinanceError { + constructor(message = "Invalid API credentials") { + super(message, ERROR_CODES.INVALID_CREDENTIALS); + this.name = "AuthenticationError"; + } +} + +/** + * Error thrown when order validation fails + */ +export class OrderValidationError extends BinanceError { + constructor( + message: string, + code: ErrorCode | number = ERROR_CODES.INVALID_PARAMETERS + ) { + super(message, code); + this.name = "OrderValidationError"; + } +} + +/** + * Error thrown when minimum notional value is not met + */ +export class MinNotionalError extends OrderValidationError { + constructor(minNotional?: string) { + super( + `Order value is too small. ${ + minNotional ? `Minimum order value is ${minNotional} USDC.` : "" + }`, + ERROR_CODES.MIN_NOTIONAL_NOT_MET + ); + this.name = "MinNotionalError"; + } +} + +/** + * Error thrown when insufficient balance + */ +export class InsufficientBalanceError extends OrderValidationError { + constructor(asset: string) { + super( + `Insufficient ${asset} balance`, + ERROR_CODES.INSUFFICIENT_BALANCE + ); + this.name = "InsufficientBalanceError"; + } +} + +/** + * Error thrown when symbol is invalid + */ +export class InvalidSymbolError extends BinanceError { + constructor(symbol: string) { + super( + `Trading pair ${symbol} is not available`, + ERROR_CODES.INVALID_PARAMETERS + ); + this.name = "InvalidSymbolError"; + } +} + +/** + * Error thrown when API request fails + */ +export class ApiError extends BinanceError { + constructor( + message: string, + code: number, + public readonly response?: unknown + ) { + super(message, code); + this.name = "ApiError"; + } +} From f67a4b51eca76920b81bcb2af3bcec7428af62b3 Mon Sep 17 00:00:00 2001 From: Luka Petrovic Date: Sat, 4 Jan 2025 14:12:29 +0100 Subject: [PATCH 26/58] chore: add readme.md --- packages/plugin-binance/README.md | 88 ++++++++++++++++++++++++++++ packages/plugin-binance/src/index.ts | 4 -- 2 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 packages/plugin-binance/README.md diff --git a/packages/plugin-binance/README.md b/packages/plugin-binance/README.md new file mode 100644 index 0000000000..6748933720 --- /dev/null +++ b/packages/plugin-binance/README.md @@ -0,0 +1,88 @@ +# Binance Plugin for Eliza + +This plugin enables Eliza to interact with the Binance cryptocurrency exchange, providing capabilities for checking prices, executing trades, and managing spot wallet balances. + +## Features + +- 📊 Real-time cryptocurrency price checks +- 💱 Spot trading (market and limit orders) +- 💰 Wallet balance inquiries +- ✅ Comprehensive error handling +- 🔒 Secure API integration + +## Prerequisites + +1. **Binance Account**: You need a Binance account to use this plugin +2. **API Keys**: Generate API keys from your Binance account: + - Go to your Binance account settings + - Navigate to API Management + - Create a new API key + - Enable spot trading permissions + - Store your API key and secret securely + +## Configuration + +Set the following environment variables: + +```env +BINANCE_API_KEY=your_api_key +BINANCE_SECRET_KEY=your_secret_key +``` + +## Installation + +Add the plugin to your Eliza configuration: + +```json +{ + "plugins": ["@elizaos/plugin-binance"] +} +``` + +## Available Actions + +The plugin provides the following actions: + +1. **GET_PRICE**: Check cryptocurrency prices + + - Example: "What's the current price of Bitcoin?" + - Example: "Check ETH price in USDT" + +2. **EXECUTE_SPOT_TRADE**: Execute spot trades + + - Example: "Buy 0.1 BTC at market price" + - Example: "Sell 100 USDT worth of ETH" + +3. **GET_SPOT_BALANCE**: Check wallet balances + - Example: "What's my BTC balance?" + - Example: "Show all my wallet balances" + +## Important Notes + +1. **API Rate Limits**: Binance implements rate limiting: + + - 1200 requests per minute for most endpoints + - Some endpoints have specific weight limits + - The plugin handles rate limiting errors appropriately + +2. **Minimum Order Sizes**: Binance enforces minimum order sizes and notional values: + + - Minimum order size varies by trading pair + - Minimum notional value (quantity × price) must be met + - The plugin validates these requirements before order execution + +3. **Error Handling**: The plugin provides detailed error messages for: + - Invalid API credentials + - Insufficient balance + - Invalid trading pairs + - Minimum notional value not met + - Other API-specific errors + +## Service Architecture + +The plugin is organized into specialized services: + +- `PriceService`: Handles price-related operations +- `TradeService`: Manages trading operations +- `AccountService`: Handles balance and account operations +- `BaseService`: Provides common functionality diff --git a/packages/plugin-binance/src/index.ts b/packages/plugin-binance/src/index.ts index 79e0bebe80..90a54d39c2 100644 --- a/packages/plugin-binance/src/index.ts +++ b/packages/plugin-binance/src/index.ts @@ -3,10 +3,6 @@ import { priceCheck } from "./actions/priceCheck"; import { spotBalance } from "./actions/spotBalance"; import { spotTrade } from "./actions/spotTrade"; -// Re-export types and services for external use -export * from "./services"; -export * from "./types"; - // Export the plugin configuration export const binancePlugin: Plugin = { name: "binance", From 85f65156f5119bf4ae953acb74fa44d1c72b7786 Mon Sep 17 00:00:00 2001 From: Proteus Date: Sat, 4 Jan 2025 15:37:19 -0500 Subject: [PATCH 27/58] expand readme description --- packages/plugin-coinprice/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-coinprice/README.md b/packages/plugin-coinprice/README.md index 7461afb69d..97644a2d6e 100644 --- a/packages/plugin-coinprice/README.md +++ b/packages/plugin-coinprice/README.md @@ -1,6 +1,6 @@ # @elizaos/plugin-coinprice -A plugin for Eliza that enables cryptocurrency price checking using the CoinMarketCap API. +A plugin for Eliza that enables cryptocurrency price checking. API provider options are CoinGecko, CoinMarketCap, and CoinCap. If no CoinGecko or CoinMarketCap API key is provided, CoinCap free API will be used. ## Features From 1d059df5bf8a3eab6ffdb2bf2823f49e3e25a76a Mon Sep 17 00:00:00 2001 From: Phlo Date: Sun, 5 Jan 2025 15:41:11 -0600 Subject: [PATCH 28/58] docs: Add "What Did You Get Done This Week? 8" notes --- docs/community/Streams/01-2025/2025-01-03.md | 127 +++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 docs/community/Streams/01-2025/2025-01-03.md diff --git a/docs/community/Streams/01-2025/2025-01-03.md b/docs/community/Streams/01-2025/2025-01-03.md new file mode 100644 index 0000000000..ddc3b83009 --- /dev/null +++ b/docs/community/Streams/01-2025/2025-01-03.md @@ -0,0 +1,127 @@ +--- +sidebar_position: 8 +title: "What Did You Get Done This Week? #8" +description: "From DeFi to Social Media: Builders Share Progress on AI Agents and Platform Integrations" +--- + +# What Did You Get Done This Week? #8 + +**From DeFi to Social Media: Builders Share Progress on AI Agents and Platform Integrations** + +- Date: 2025-01-03 +- Twitter Spaces: https://x.com/i/spaces/1RDGlygdXVNJL +- YouTube Link: https://www.youtube.com/watch?v=Vs7D5DN_trk + +## Summary + +**Structure and Format:** + +* The space was hosted by Jin (on @ai16zdao) and co-hosted by Shaw, who was initially facing audio issues. +* It followed a 2-minute round format for updates, focusing on accomplishments related to open-source AI or AI agents. +* Participants were encouraged to comment below the post if they couldn't speak. +* A separate demo day was planned for projects needing screen sharing. + +**Key Updates and Themes:** + +* **Agent Development and Deployment:** A significant portion of the updates focused on developing, refining, and deploying AI agents. Many participants used the Eliza framework, but others were creating their own frameworks. +* **Platform Integration**: Many participants were focused on integrating their agents into specific platforms like Twitter, Telegram, Discord, and web apps, including new platforms like modes and base. +* **Focus on User Experience:** A common theme was making AI agents user-friendly and accessible to those without coding experience. Many were creating tools or platforms for others to easily create, deploy, and use AI agents. +* **AI-Driven Content Generation:** Several developers were building agents focused on media creation, including songs, videos, and images, as well as content creation from Twitter posts and Github repos. +* **Integration of Financial Systems:** Several people were developing agents for trading and financial management, including integrations with DeFi protocols. +* **Security and Auditing:** Some projects focused on using AI for Web3 security, including auditing smart contracts and creating security tools. +* **Community and Open Source:** Several people mentioned the importance of the community and open source aspect for their projects. +* **The Importance of Social Media Marketing:** Several people discussed how AI and agents should be a core part of your marketing and product strategy going forward. +* **Multi-Agent Systems:** Some developers were exploring multi-agent workflows and communication, demonstrating a growing interest in complex AI interactions. +* **Data Handling and Validation:** Some developers were trying to optimize data gathering, validation, and more precise data handling when working with LLMs. +* **Real-World Applications:** Some participants were working on real world applications, specifically in the areas of climate change and also fashion and augmented reality. +* **Integration with Other Services:** Participants were also exploring integration with other services such as Eleven Labs and other web3 protocols like Story Protocol. + +**Other Interesting Points:** + +* The hosts are actively exploring ways to integrate more AI agents into their platforms, potentially leading to agent-led spaces. +* There's a sense of collaborative spirit and willingness to help each other among the community members. +* The space demonstrated a lot of interest in turning existing tools into agents, as well as building agents from scratch +* Some participants are attempting to automate parts of the development cycle, especially with planning, PR reviews, and documentation. + + +## Hot Takes + +- **Web3 and Agent Integration** + > "I think getting web 2 people to realize that this is actually just an agent framework you can build apps with is like definitely my goal it doesn't have to be a web3 thing but it's cool that when it is too you know like I think crypto's got a great incentive structure." - *shawmakesmagic* [00:38:17] + +- **AI Marketing Takeover** + > "I think that in the future anyone who doesn't have an agent shilling their thing on social media is probably going to have a really hard time marketing their product and I think it's just got to be part of your product strategy now." - *shawmakesmagic* [00:38:48] + +- **Leveraging Developing Countries for AI Labor** + > "There is no reason why we cannot leverage the power of people in the third world to build AI agents for us. We in the West are lazy. We have it too easy." - *NEETOCRACY* [01:25:23] + +- **AI Replacing Human Interaction** + > "It's gonna be weird when, like, our great-grandchildren are talking to our parents, you know, it's gonna be, like, as, as, like, our ancestors generally, like, you know, that generations of people far down the future will know what we were like because all of our data and our voice and everything about us will be, like, preserved in this kind of agents that they can talk to. It's going to be very interesting." - *shawmakesmagic* [01:18:44] + +- **The Challenges of Getting AI Agents to Work in the Real World** + > "But, uh, what ended up happening was messing around with, like, DMs, and DMing people, she got suspended. So basically navigating that whole situation, I was like, you know what, this is actually an opportunity to try some things here." - *O_on_X* [02:27:39] + + +## Timestamps + +- [00:00:55]() - **ai16zdao**: Introduction and format of the space (2-minute rounds, focus on open source AI and AI agents). +- [00:04:43]() - **shawmakesmagic**: Purpose of the space, accountability and updates on weekly progress. +- [00:06:28]() - **astridhpilla**: Update on Miku chatbot, relaunch, and plans for CES. +- [00:09:48]() - **lostgirldev**: Update on Selene's growth, PR review feature, GitLarp launch, and community engagement. +- [00:12:57]() - **spaceodili**: Update on Eliza framework fixes, voice features, and plugin process isolation. +- [00:14:19]() - **0xBuildInPublic**: Update on Audits agent, automated plugin documentation, and plans for a white hat security DAO. +- [00:17:42]() - **youfadedwealth**: Update on PP coin (automated AI trading companion) and SendAI agent toolkit. +- [00:19:57]() - **nftRanch**: Update on integrating their framework with Eliza and plans for Broke. +- [00:21:56]() - **SYMBiEX**: Update on adding agents to the arena, DeepSeek model provider, and character creation plugin. +- [00:22:54]() - **SuperfruitsAi**: Update on Dragon Fruit AI launch, user growth, and upcoming features (Chrome extension, Telegram bot). +- [00:24:55]() - **TimshelXYZ**: Introduction of Meetup Fund (Eliza design and hosting platform) and their invite code system. +- [00:27:05]() - **chrislatorres**: Update on Eliza partnerships, docs workflow, and core V2 contributor discussions. +- [00:29:05]() - **AIFlow_ML**: Update on knowledge graph for repos and a project to add more memories. +- [00:30:24]() - **jamesyoung**: Update on MotherDAO, verifiable inference system, and AI agent starter kit using Lit Actions. +- [00:33:16]() - **deadlock_1991**: Update on Alice AI (fund management agent), trading capabilities, and optimization efforts. +- [00:36:16]() - **yeahimomar**: Update on Pixocracy (Minecraft village management with AI agents) and plans for a launchpad. +- [00:39:44]() - **human_for_now**: Update on new form fill infrastructure code for Eliza core. +- [00:42:11]() - **lordasado**: Update on Smol World, agent reasoning, mini-games, and plans for an ElizaCon. +- [00:44:26]() - **RodrigoSotoAlt**: Update on memory management for Bosu and his new role as a greeter in the ai16z Discord. +- [00:45:49]() - **HDPbilly**: Update on extending database adapters, Twitter client, and creating a reflection loop for autonomous behavior. +- [00:50:25]() - **GoatOfGamblers**: Update on Goat AGI, Goat Arena launch, Goatuchan agent, and plans for an Eliza plugin. +- [00:53:37]() - **Titan_Node**: Update on integrating LivePeer endpoints for free inference and plans for a real-time video AI plugin. +- [00:55:35]() - **KyleSt4rgarden**: Update on open-sourcing a Solana agent token staking program (Devotion) and a broader effort to build open-source smart contracts and tools for agents. +- [00:58:28]() - **unl__cky**: Update on improving media generation for Escapism (art agent) with a focus on music and video. +- [01:00:19]() - **CheddarQueso3D**: Update on creating documentation for Eliza plugins and developing two characters (DAO and cannabis cultivation consultants). +- [01:03:15]() - **sunosuporno**: Update on launching the waitlist for Midas (DeFi assistant) and its features. +- [01:07:31]() - **tmoindustries**: Update on launching onchainagents.ai, region swarm, and progress on voice integration. +- [01:10:30]() - **Sawyer_APRO**: Update on integrating with BNB Chain, launching an HTTPS agent solution, and plans to collaborate with ai16z. +- [01:13:02]() - **wakesync**: Update on Eliza's Netflix and chill extension, token gating, hardware partnership, and Twitter integrations. +- [01:15:51]() - **Ru7Longcrypto**: Discussion about creating an AI companion similar to the movie "Her" and potential applications. +- [01:21:04]() - **marko_post**: Update on No 1 on Mars (Mars' first digital citizen), multi-agent system, dual memory system, and story generation. +- [01:23:41]() - **NEETOCRACY**: Discussion about building a DAO called Army of Indians to leverage Indian labor for AI agent development. +- [01:25:59]() - **HefAiGent**: Introduction to HefAiGent built using the Eliza framework, plans for ERC 314 technology, and appreciation for the community. +- [01:28:43]() - **reality_spiral**: Update on GitHub client, agent participation in scrum planning, and a scenario system for evaluating agent performance. +- [01:33:41]() - **witconomist**: Update on the Marketplace of Trust (white paper), its purpose, and how to get involved. +- [01:36:28]() - **triadfi**: Update on expanding hype and flop personalities for their agents and progressing on independent market creation and resolution. +- [01:37:53]() - **Rowdymode**: Update on Twin Tone, white paper draft, and beta testing with creators. +- [01:39:57]() - **MaushishYadav**: Update on Elris (yield optimizing agent), beta testing applications, local repository, and token launch. +- [01:41:07]() - **chaininsured**: Update on using an Eliza agent as an insurance broker, collecting data, and providing quotes. +- [01:46:47]() - **godfreymeyer**: Update on production, animations, showrunner setup, and progress on the news show using 3D avatars. +- [01:52:19]() - **thelotioncoin**: Update on Lotion, allowing users to deploy AI agents on social channels and websites, and focusing on integration and customization. +- [01:54:57]() - **codergf_xyz**: Update on CoderGF, creating a Twitter bot (Haruka), and plans to make it easier for normies to deploy bots. +- [02:00:44]() - **IGLIVISION**: Update on building an NFT marketplace on the Superchain and integrating with Nebula and other API providers. +- [02:02:51]() - **EledraNguyen**: Update on Square Fun AI, analyzing data from the Solana AI Hackathon, and plans for developer productivity analysis. +- [02:08:49]() - **GnonOnSolana**: Update on Echo Chambers v2.3, simplified agent building, multimodal stepping, performance improvements, and integration with ZeroPi. +- [02:13:26]() - **Satoshi_BTCFi**: Inquiry about Bitcoin, Lightning, and Taproot integration in Eliza. +- [02:15:55]() - **swarmnode**: Update on Swarm Node's growth, team expansion, and the launch of a bounties feature. +- [02:18:49]() - **memeillionaire**: Discussion about integrating with Griffin and the DAO's fund platform. +- [02:21:29]() - **krauscrypto**: Discussion about AI voice cloning and integrating it into a mobile app, and interest in applying it to Eliza. +- [02:23:19]() - **usebuildfun**: Update on launching a no-code AI agent builder with custom API functions. +- [02:25:44]() - **affaanmustafa**: Update on a project with unprecedented growth and lessons learned about scaling and team expansion. +- [02:27:24]() - **O_on_X**: Update on Eliza's sister getting suspended due to DMs and using Playwright and Grok Vision for unsuspension. +- [02:29:44]() - **AITATsol**: Update on AI Tag, data collection for global trade analysis, and the need for data analysts. +- [02:33:19]() - **xiao_zcloak**: Update on a PR for a plugin that allows agents to send money on social platforms without asking for wallet addresses. +- [02:34:15]() - **Protocol_Blend**: Update on integrating an AI agent into a DeFi protocol to smooth user experience and plans for listing on MEXC. +- [02:35:55]() - **yq_acc**: Update on Autonome, a platform for launching Eliza agents in a verifiable environment, and submitting PRs to fix issues. +- [02:38:04]() - **akshayynft**: Inquiry about getting into AI agent development and seeking guidance. +- [02:38:40]() - **BenjiStackzzz**: Mention of Quinn and its potential in the AI agent space. +- [02:39:49]() - **0xBuns**: Offer to assist with teaching and building AI agents. +- [02:41:10]() - **aiquantfun**: Update on building a specialized launchpad for autonomous AI quant trading using the Eliza framework. +- [02:42:44]() - **ai16zdao**: Closing remarks and invitation to join next week. From 6b29febaa350c7c8d1595f2f74f953c410d69631 Mon Sep 17 00:00:00 2001 From: Bertrand Juglas Date: Thu, 2 Jan 2025 15:26:23 +0100 Subject: [PATCH 29/58] feat: add Arthera plugin from EVM plugin model feat: rename plugin from EVM to Arthera and update README feat: remove bridge and swap actions from Arthera plugin feat: update transfer examples and templates to use AA instead of ETH feat: update viem dependency to version 2.21.58 and adjust pnpm-lock.yaml feat: remove unused LiFi dependencies and clean up type definitions in Arthera plugin feat: remove bridge actions and templates from Arthera plugin feat: remove swap actions and templates from Arthera plugin feat: update EVM naming to Arthera feat: update README and types for Arthera mainnet integration feat: update plugin to use Arthera instead of mainnet fix: add required devDependencies fix: remove switchChain fix: update _options type to Record in transferAction fix: correct log message format in transfer action to include wallet client address test: enhance transfer tests with additional wallet provider and address validation Plugin arthera merge (#3) * feat: added arthera to default character and agent * feat: renamed EVM_PRIVATE_KEY by ARTHERA_PRIVATE_KEY * fix: roll back core package * fix: workspace: version --------- Co-authored-by: Arthera Node fix: run transfer test only if private key provided fix: add missing newline at end of package.json and tsconfig.json files --- .env.example | 3 + agent/package.json | 1 + agent/src/index.ts | 4 + packages/plugin-arthera/README.md | 68 ++++++ packages/plugin-arthera/eslint.config.mjs | 3 + packages/plugin-arthera/package.json | 24 +++ .../plugin-arthera/src/actions/transfer.ts | 176 +++++++++++++++ packages/plugin-arthera/src/index.ts | 18 ++ .../plugin-arthera/src/providers/wallet.ts | 203 ++++++++++++++++++ .../plugin-arthera/src/templates/index.ts | 23 ++ .../plugin-arthera/src/tests/transfer.test.ts | 83 +++++++ .../plugin-arthera/src/tests/wallet.test.ts | 175 +++++++++++++++ packages/plugin-arthera/src/types/index.ts | 73 +++++++ packages/plugin-arthera/tsconfig.json | 15 ++ packages/plugin-arthera/tsup.config.ts | 20 ++ pnpm-lock.yaml | 19 ++ 16 files changed, 908 insertions(+) create mode 100644 packages/plugin-arthera/README.md create mode 100644 packages/plugin-arthera/eslint.config.mjs create mode 100644 packages/plugin-arthera/package.json create mode 100644 packages/plugin-arthera/src/actions/transfer.ts create mode 100644 packages/plugin-arthera/src/index.ts create mode 100644 packages/plugin-arthera/src/providers/wallet.ts create mode 100644 packages/plugin-arthera/src/templates/index.ts create mode 100644 packages/plugin-arthera/src/tests/transfer.test.ts create mode 100644 packages/plugin-arthera/src/tests/wallet.test.ts create mode 100644 packages/plugin-arthera/src/types/index.ts create mode 100644 packages/plugin-arthera/tsconfig.json create mode 100644 packages/plugin-arthera/tsup.config.ts diff --git a/.env.example b/.env.example index 3cb15a09af..f128a461e1 100644 --- a/.env.example +++ b/.env.example @@ -195,6 +195,9 @@ EVM_PROVIDER_URL= AVALANCHE_PRIVATE_KEY= AVALANCHE_PUBLIC_KEY= +# Arthera +ARTHERA_PRIVATE_KEY= + # Solana SOLANA_PRIVATE_KEY= SOLANA_PUBLIC_KEY= diff --git a/agent/package.json b/agent/package.json index 1cda1e6103..12c7aa4bdd 100644 --- a/agent/package.json +++ b/agent/package.json @@ -66,6 +66,7 @@ "@elizaos/plugin-avalanche": "workspace:*", "@elizaos/plugin-web-search": "workspace:*", "@elizaos/plugin-genlayer": "workspace:*", + "@elizaos/plugin-arthera": "workspace:*", "readline": "1.3.0", "ws": "8.18.0", "yargs": "17.7.2" diff --git a/agent/src/index.ts b/agent/src/index.ts index e4f6ccf1c9..6c1ed9ec7c 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -67,6 +67,7 @@ import { webSearchPlugin } from "@elizaos/plugin-web-search"; import { stargazePlugin } from "@elizaos/plugin-stargaze"; import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era"; import { availPlugin } from "@elizaos/plugin-avail"; +import { artheraPlugin } from "@elizaos/plugin-arthera"; import Database from "better-sqlite3"; import fs from "fs"; import net from "net"; @@ -624,6 +625,9 @@ export async function createAgent( : null, getSecret(character, "AVAIL_SEED") ? availPlugin : null, getSecret(character, "AVAIL_APP_ID") ? availPlugin : null, + getSecret(character, "ARTHERA_PRIVATE_KEY")?.startsWith("0x") + ? artheraPlugin + : null, ].filter(Boolean), providers: [], actions: [], diff --git a/packages/plugin-arthera/README.md b/packages/plugin-arthera/README.md new file mode 100644 index 0000000000..b634635d46 --- /dev/null +++ b/packages/plugin-arthera/README.md @@ -0,0 +1,68 @@ +# `@elizaos/plugin-arthera` + +This plugin provides actions and providers for interacting with Arthera. + +--- + +## Configuration + +### Default Setup + +By default, **Arthera** is enabled. To use it, simply add your private key to the `.env` file: + +```env +ARTHERA_PRIVATE_KEY=your-private-key-here +``` + +### Custom RPC URLs + +By default, the RPC URL is inferred from the `viem/chains` config. To use a custom RPC URL for a specific chain, add the following to your `.env` file: + +```env +ETHEREUM_PROVIDER_=https://your-custom-rpc-url +``` + +**Example usage:** + +```env +ETHEREUM_PROVIDER_ARTHERA=https://rpc.arthera.net +``` + +## Provider + +The **Wallet Provider** initializes with Arthera. It: + +- Provides the **context** of the currently connected address and its balance. +- Creates **Public** and **Wallet clients** to interact with the supported chain. + +--- + +## Actions + +### Transfer + +Transfer tokens from one address to another on Arthera. Just specify the: + +- **Amount** +- **Chain** +- **Recipient Address** + +**Example usage:** + +```bash +Transfer 1 AA to 0xRecipient on arthera. +``` + +--- + +## Contribution + +The plugin contains tests. Whether you're using **TDD** or not, please make sure to run the tests before submitting a PR. + +### Running Tests + +Navigate to the `plugin-arthera` directory and run: + +```bash +pnpm test +``` diff --git a/packages/plugin-arthera/eslint.config.mjs b/packages/plugin-arthera/eslint.config.mjs new file mode 100644 index 0000000000..92fe5bbebe --- /dev/null +++ b/packages/plugin-arthera/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-arthera/package.json b/packages/plugin-arthera/package.json new file mode 100644 index 0000000000..3431ecf67e --- /dev/null +++ b/packages/plugin-arthera/package.json @@ -0,0 +1,24 @@ +{ + "name": "@elizaos/plugin-arthera", + "version": "0.1.7-alpha.2", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "tsup": "8.3.5", + "viem": "2.21.58" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "test": "vitest run", + "lint": "eslint --fix --cache ." + }, + "devDependencies": { + "whatwg-url": "7.1.0" + }, + "peerDependencies": { + "whatwg-url": "7.1.0" + } +} diff --git a/packages/plugin-arthera/src/actions/transfer.ts b/packages/plugin-arthera/src/actions/transfer.ts new file mode 100644 index 0000000000..2ad25281de --- /dev/null +++ b/packages/plugin-arthera/src/actions/transfer.ts @@ -0,0 +1,176 @@ +import { ByteArray, formatEther, parseEther, type Hex } from "viem"; +import { + composeContext, + generateObjectDeprecated, + HandlerCallback, + ModelClass, + type IAgentRuntime, + type Memory, + type State, +} from "@elizaos/core"; + +import { initWalletProvider, WalletProvider } from "../providers/wallet"; +import type { Transaction, TransferParams } from "../types"; +import { transferTemplate } from "../templates"; + +export { transferTemplate }; + +// Exported for tests +export class TransferAction { + constructor(private walletProvider: WalletProvider) {} + + async transfer(params: TransferParams): Promise { + const walletClient = this.walletProvider.getWalletClient( + params.fromChain + ); + + console.log( + `Transferring: ${params.amount} tokens from (${walletClient.account.address} to (${params.toAddress} on ${params.fromChain})` + ); + + if (!params.data) { + params.data = "0x"; + } + + try { + const hash = await walletClient.sendTransaction({ + account: walletClient.account, + to: params.toAddress, + value: parseEther(params.amount), + data: params.data as Hex, + kzg: { + blobToKzgCommitment: function (_: ByteArray): ByteArray { + throw new Error("Function not implemented."); + }, + computeBlobKzgProof: function ( + _blob: ByteArray, + _commitment: ByteArray + ): ByteArray { + throw new Error("Function not implemented."); + }, + }, + chain: undefined, + }); + + return { + hash, + from: walletClient.account.address, + to: params.toAddress, + value: parseEther(params.amount), + data: params.data as Hex, + }; + } catch (error) { + throw new Error(`Transfer failed: ${error.message}`); + } + } +} + +const buildTransferDetails = async ( + state: State, + runtime: IAgentRuntime, + wp: WalletProvider +): Promise => { + const context = composeContext({ + state, + template: transferTemplate, + }); + + const chains = Object.keys(wp.chains); + + const contextWithChains = context.replace( + "SUPPORTED_CHAINS", + chains.map((item) => `"${item}"`).join("|") + ); + + const transferDetails = (await generateObjectDeprecated({ + runtime, + context: contextWithChains, + modelClass: ModelClass.SMALL, + })) as TransferParams; + + const existingChain = wp.chains[transferDetails.fromChain]; + + if (!existingChain) { + throw new Error( + "The chain " + + transferDetails.fromChain + + " not configured yet. Add the chain or choose one from configured: " + + chains.toString() + ); + } + + return transferDetails; +}; + +export const transferAction = { + name: "transfer", + description: "Transfer tokens between addresses on the same chain", + handler: async ( + runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: Record, + callback?: HandlerCallback + ) => { + console.log("Transfer action handler called"); + const walletProvider = initWalletProvider(runtime); + const action = new TransferAction(walletProvider); + + // Compose transfer context + const paramOptions = await buildTransferDetails( + state, + runtime, + walletProvider + ); + + try { + const transferResp = await action.transfer(paramOptions); + if (callback) { + callback({ + text: `Successfully transferred ${paramOptions.amount} tokens to ${paramOptions.toAddress}\nTransaction Hash: ${transferResp.hash}`, + content: { + success: true, + hash: transferResp.hash, + amount: formatEther(transferResp.value), + recipient: transferResp.to, + chain: paramOptions.fromChain, + }, + }); + } + return true; + } catch (error) { + console.error("Error during token transfer:", error); + if (callback) { + callback({ + text: `Error transferring tokens: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + template: transferTemplate, + validate: async (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting("ARTHERA_PRIVATE_KEY"); + return typeof privateKey === "string" && privateKey.startsWith("0x"); + }, + examples: [ + [ + { + user: "assistant", + content: { + text: "I'll help you transfer 1 AA to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e", + action: "SEND_TOKENS", + }, + }, + { + user: "user", + content: { + text: "Transfer 1 AA to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e", + action: "SEND_TOKENS", + }, + }, + ], + ], + similes: ["SEND_TOKENS", "TOKEN_TRANSFER", "MOVE_TOKENS"], +}; diff --git a/packages/plugin-arthera/src/index.ts b/packages/plugin-arthera/src/index.ts new file mode 100644 index 0000000000..3fe8d58594 --- /dev/null +++ b/packages/plugin-arthera/src/index.ts @@ -0,0 +1,18 @@ +export * from "./actions/transfer"; +export * from "./providers/wallet"; +export * from "./types"; + +import type { Plugin } from "@elizaos/core"; +import { transferAction } from "./actions/transfer"; +import { artheraWalletProvider } from "./providers/wallet"; + +export const artheraPlugin: Plugin = { + name: "arthera", + description: "Arthera blockchain integration plugin", + providers: [artheraWalletProvider], + evaluators: [], + services: [], + actions: [transferAction], +}; + +export default artheraPlugin; diff --git a/packages/plugin-arthera/src/providers/wallet.ts b/packages/plugin-arthera/src/providers/wallet.ts new file mode 100644 index 0000000000..7d724fbf4a --- /dev/null +++ b/packages/plugin-arthera/src/providers/wallet.ts @@ -0,0 +1,203 @@ +import { + createPublicClient, + createWalletClient, + formatUnits, + http, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import type { IAgentRuntime, Provider, Memory, State } from "@elizaos/core"; +import type { + Address, + WalletClient, + PublicClient, + Chain, + HttpTransport, + Account, + PrivateKeyAccount, +} from "viem"; +import * as viemChains from "viem/chains"; + +import type { SupportedChain } from "../types"; + +export class WalletProvider { + private currentChain: SupportedChain = "arthera"; + chains: Record = { arthera: viemChains.arthera }; + account: PrivateKeyAccount; + + constructor(privateKey: `0x${string}`, chains?: Record) { + this.setAccount(privateKey); + this.setChains(chains); + + if (chains && Object.keys(chains).length > 0) { + this.setCurrentChain(Object.keys(chains)[0] as SupportedChain); + } + } + + getAddress(): Address { + return this.account.address; + } + + getCurrentChain(): Chain { + return this.chains[this.currentChain]; + } + + getPublicClient( + chainName: SupportedChain + ): PublicClient { + const transport = this.createHttpTransport(chainName); + + const publicClient = createPublicClient({ + chain: this.chains[chainName], + transport, + }); + return publicClient; + } + + getWalletClient(chainName: SupportedChain): WalletClient { + const transport = this.createHttpTransport(chainName); + + const walletClient = createWalletClient({ + chain: this.chains[chainName], + transport, + account: this.account, + }); + + return walletClient; + } + + getChainConfigs(chainName: SupportedChain): Chain { + const chain = viemChains[chainName]; + + if (!chain?.id) { + throw new Error("Invalid chain name"); + } + + return chain; + } + + async getWalletBalance(): Promise { + try { + const client = this.getPublicClient(this.currentChain); + const balance = await client.getBalance({ + address: this.account.address, + }); + return formatUnits(balance, 18); + } catch (error) { + console.error("Error getting wallet balance:", error); + return null; + } + } + + async getWalletBalanceForChain( + chainName: SupportedChain + ): Promise { + try { + const client = this.getPublicClient(chainName); + const balance = await client.getBalance({ + address: this.account.address, + }); + return formatUnits(balance, 18); + } catch (error) { + console.error("Error getting wallet balance:", error); + return null; + } + } + + private setAccount = (pk: `0x${string}`) => { + this.account = privateKeyToAccount(pk); + }; + + private setChains = (chains?: Record) => { + if (!chains) { + return; + } + Object.keys(chains).forEach((chain: string) => { + this.chains[chain] = chains[chain]; + }); + }; + + private setCurrentChain = (chain: SupportedChain) => { + this.currentChain = chain; + }; + + private createHttpTransport = (chainName: SupportedChain) => { + const chain = this.chains[chainName]; + + if (chain.rpcUrls.custom) { + return http(chain.rpcUrls.custom.http[0]); + } + return http(chain.rpcUrls.default.http[0]); + }; + + static genChainFromName( + chainName: string, + customRpcUrl?: string | null + ): Chain { + const baseChain = viemChains[chainName]; + + if (!baseChain?.id) { + throw new Error("Invalid chain name"); + } + + const viemChain: Chain = customRpcUrl + ? { + ...baseChain, + rpcUrls: { + ...baseChain.rpcUrls, + custom: { + http: [customRpcUrl], + }, + }, + } + : baseChain; + + return viemChain; + } +} + +const genChainsFromRuntime = ( + runtime: IAgentRuntime +): Record => { + const chainNames = ["arthera"]; + const chains = {}; + + chainNames.forEach((chainName) => { + const rpcUrl = runtime.getSetting( + "ETHEREUM_PROVIDER_" + chainName.toUpperCase() + ); + const chain = WalletProvider.genChainFromName(chainName, rpcUrl); + chains[chainName] = chain; + }); + + return chains; +}; + +export const initWalletProvider = (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting("ARTHERA_PRIVATE_KEY"); + if (!privateKey) { + throw new Error("ARTHERA_PRIVATE_KEY is missing"); + } + + const chains = genChainsFromRuntime(runtime); + + return new WalletProvider(privateKey as `0x${string}`, chains); +}; + +export const artheraWalletProvider: Provider = { + async get( + runtime: IAgentRuntime, + _message: Memory, + _state?: State + ): Promise { + try { + const walletProvider = initWalletProvider(runtime); + const address = walletProvider.getAddress(); + const balance = await walletProvider.getWalletBalance(); + const chain = walletProvider.getCurrentChain(); + return `Arthera Wallet Address: ${address}\nBalance: ${balance} ${chain.nativeCurrency.symbol}\nChain ID: ${chain.id}, Name: ${chain.name}`; + } catch (error) { + console.error("Error in Arthera wallet provider:", error); + return null; + } + }, +}; diff --git a/packages/plugin-arthera/src/templates/index.ts b/packages/plugin-arthera/src/templates/index.ts new file mode 100644 index 0000000000..d8206074bc --- /dev/null +++ b/packages/plugin-arthera/src/templates/index.ts @@ -0,0 +1,23 @@ +export const transferTemplate = `Given the recent messages and wallet information below: + +{{recentMessages}} + +{{walletInfo}} + +Extract the following information about the requested transfer: +- Chain to execute on: Must be one of ["arthera", "base", ...] (like in viem/chains) +- Amount to transfer: Must be a string representing the amount in AA (only number without coin symbol, e.g., "0.1") +- Recipient address: Must be a valid Arthera address starting with "0x" +- Token symbol or address (if not native token): Optional, leave as null for AA transfers + +Respond with a JSON markdown block containing only the extracted values. All fields except 'token' are required: + +\`\`\`json +{ + "fromChain": SUPPORTED_CHAINS, + "amount": string, + "toAddress": string, + "token": string | null +} +\`\`\` +`; diff --git a/packages/plugin-arthera/src/tests/transfer.test.ts b/packages/plugin-arthera/src/tests/transfer.test.ts new file mode 100644 index 0000000000..aaecf38d7c --- /dev/null +++ b/packages/plugin-arthera/src/tests/transfer.test.ts @@ -0,0 +1,83 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { generatePrivateKey } from "viem/accounts"; +import { Chain } from "viem"; +import { getEnvVariable } from "@elizaos/core"; + +import { TransferAction } from "../actions/transfer"; +import { WalletProvider } from "../providers/wallet"; + +describe("Transfer Action", () => { + let wp: WalletProvider; + let wp1: WalletProvider; + + beforeEach(async () => { + const pk = generatePrivateKey(); + const pk1 = getEnvVariable("ARTHERA_PRIVATE_KEY") as `0x${string}`; + const customChains = prepareChains(); + wp = new WalletProvider(pk, customChains); + if (pk1) { + wp1 = new WalletProvider(pk1, customChains); + } + }); + describe("Constructor", () => { + it("should initialize with wallet provider", () => { + const ta = new TransferAction(wp); + + expect(ta).toBeDefined(); + }); + }); + describe("Transfer", () => { + let ta: TransferAction; + let ta1: TransferAction; + let receiverAddress: `0x${string}`; + + beforeEach(() => { + ta = new TransferAction(wp); + if (wp1) { + ta1 = new TransferAction(wp1); + receiverAddress = wp1.getAddress(); + } + else { + receiverAddress = wp.getAddress(); + } + }); + + it("throws if not enough gas", async () => { + await expect( + ta.transfer({ + fromChain: "arthera", + toAddress: receiverAddress, + amount: "1", + }) + ).rejects.toThrow( + "Transfer failed: The total cost (gas * gas fee + value) of executing this transaction exceeds the balance of the account." + ); + }); + + if (wp1) { + it("transfers tokens", async () => { + const tx = await ta1.transfer({ + fromChain: "arthera", + toAddress: receiverAddress, + amount: "0.001", + }); + + expect(tx).toBeDefined(); + expect(tx.from).toEqual(wp1.getAddress()); + expect(tx.to).toEqual(receiverAddress); + expect(tx.value).toEqual(1000000000000000n); + }); + } + }); +}); + +const prepareChains = () => { + const customChains: Record = {}; + const chainNames = ["arthera"]; + chainNames.forEach( + (chain) => + (customChains[chain] = WalletProvider.genChainFromName(chain)) + ); + + return customChains; +}; diff --git a/packages/plugin-arthera/src/tests/wallet.test.ts b/packages/plugin-arthera/src/tests/wallet.test.ts new file mode 100644 index 0000000000..07cb1494ed --- /dev/null +++ b/packages/plugin-arthera/src/tests/wallet.test.ts @@ -0,0 +1,175 @@ +import { describe, it, expect, beforeAll, beforeEach } from "vitest"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { arthera, Chain } from "viem/chains"; + +import { WalletProvider } from "../providers/wallet"; + +const customRpcUrls = { + arthera: "custom-rpc.arthera.io", +}; + +describe("Wallet provider", () => { + let walletProvider: WalletProvider; + let pk: `0x${string}`; + const customChains: Record = {}; + + beforeAll(() => { + pk = generatePrivateKey(); + + const chainNames = ["arthera"]; + chainNames.forEach( + (chain) => + (customChains[chain] = WalletProvider.genChainFromName(chain)) + ); + }); + + describe("Constructor", () => { + it("sets address", () => { + const account = privateKeyToAccount(pk); + const expectedAddress = account.address; + + walletProvider = new WalletProvider(pk); + + expect(walletProvider.getAddress()).toEqual(expectedAddress); + }); + it("sets default chain to arthera", () => { + walletProvider = new WalletProvider(pk); + + expect(walletProvider.chains.arthera.id).toEqual(arthera.id); + expect(walletProvider.getCurrentChain().id).toEqual(arthera.id); + }); + it("sets custom chains", () => { + walletProvider = new WalletProvider(pk, customChains); + + expect(walletProvider.chains.arthera.id).toEqual(arthera.id); + }); + it("sets the first provided custom chain as current chain", () => { + walletProvider = new WalletProvider(pk, customChains); + + expect(walletProvider.getCurrentChain().id).toEqual(arthera.id); + }); + }); + describe("Clients", () => { + beforeEach(() => { + walletProvider = new WalletProvider(pk); + }); + it("generates public client", () => { + const client = walletProvider.getPublicClient("arthera"); + expect(client.chain.id).toEqual(arthera.id); + expect(client.transport.url).toEqual( + arthera.rpcUrls.default.http[0] + ); + }); + it("generates public client with custom rpcurl", () => { + const chain = WalletProvider.genChainFromName( + "arthera", + customRpcUrls.arthera + ); + const wp = new WalletProvider(pk, { ["arthera"]: chain }); + + const client = wp.getPublicClient("arthera"); + expect(client.chain.id).toEqual(arthera.id); + expect(client.chain.rpcUrls.default.http[0]).toEqual( + arthera.rpcUrls.default.http[0] + ); + expect(client.chain.rpcUrls.custom.http[0]).toEqual( + customRpcUrls.arthera + ); + expect(client.transport.url).toEqual(customRpcUrls.arthera); + }); + it("generates wallet client", () => { + const account = privateKeyToAccount(pk); + const expectedAddress = account.address; + + const client = walletProvider.getWalletClient("arthera"); + + expect(client.account.address).toEqual(expectedAddress); + expect(client.transport.url).toEqual( + arthera.rpcUrls.default.http[0] + ); + }); + it("generates wallet client with custom rpcurl", () => { + const account = privateKeyToAccount(pk); + const expectedAddress = account.address; + const chain = WalletProvider.genChainFromName( + "arthera", + customRpcUrls.arthera + ); + const wp = new WalletProvider(pk, { ["arthera"]: chain }); + + const client = wp.getWalletClient("arthera"); + + expect(client.account.address).toEqual(expectedAddress); + expect(client.chain.id).toEqual(arthera.id); + expect(client.chain.rpcUrls.default.http[0]).toEqual( + arthera.rpcUrls.default.http[0] + ); + expect(client.chain.rpcUrls.custom.http[0]).toEqual( + customRpcUrls.arthera + ); + expect(client.transport.url).toEqual(customRpcUrls.arthera); + }); + }); + describe("Balance", () => { + beforeEach(() => { + walletProvider = new WalletProvider(pk, customChains); + }); + it("should fetch balance", async () => { + const bal = await walletProvider.getWalletBalance(); + + expect(bal).toEqual("0"); + }); + it("should fetch balance for a specific added chain", async () => { + const bal = await walletProvider.getWalletBalanceForChain("arthera"); + + expect(bal).toEqual("0"); + }); + it("should return null if chain is not added", async () => { + const bal = await walletProvider.getWalletBalanceForChain("base"); + expect(bal).toBeNull(); + }); + }); + describe("Chain", () => { + beforeEach(() => { + walletProvider = new WalletProvider(pk, customChains); + }); + it("generates chains from chain name", () => { + const chainName = "arthera"; + const chain: Chain = WalletProvider.genChainFromName(chainName); + + expect(chain.rpcUrls.default.http[0]).toEqual( + arthera.rpcUrls.default.http[0] + ); + }); + it("generates chains from chain name with custom rpc url", () => { + const chainName = "arthera"; + const customRpcUrl = customRpcUrls.arthera; + const chain: Chain = WalletProvider.genChainFromName( + chainName, + customRpcUrl + ); + + expect(chain.rpcUrls.default.http[0]).toEqual( + arthera.rpcUrls.default.http[0] + ); + expect(chain.rpcUrls.custom.http[0]).toEqual(customRpcUrl); + }); + it("gets chain configs", () => { + const chain = walletProvider.getChainConfigs("arthera"); + + expect(chain.id).toEqual(arthera.id); + }); + it("throws if unsupported chain name", () => { + // intentionally set unsupported chain, ts will complain + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(() => WalletProvider.genChainFromName("ethereum")).toThrow(); + }); + it("throws if invalid chain name", () => { + // intentionally set incorrect chain, ts will complain + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(() => WalletProvider.genChainFromName("eth")).toThrow(); + }); + }); +}); diff --git a/packages/plugin-arthera/src/types/index.ts b/packages/plugin-arthera/src/types/index.ts new file mode 100644 index 0000000000..f772c842a6 --- /dev/null +++ b/packages/plugin-arthera/src/types/index.ts @@ -0,0 +1,73 @@ +import type { + Account, + Address, + Chain, + Hash, + HttpTransport, + PublicClient, + WalletClient, +} from "viem"; +import * as viemChains from "viem/chains"; + +const _SupportedChainList = Object.keys(viemChains) as Array< + keyof typeof viemChains +>; +export type SupportedChain = (typeof _SupportedChainList)[number]; + +// Transaction types +export interface Transaction { + hash: Hash; + from: Address; + to: Address; + value: bigint; + data?: `0x${string}`; + chainId?: number; +} + +// Chain configuration +export interface ChainMetadata { + chainId: number; + name: string; + chain: Chain; + rpcUrl: string; + nativeCurrency: { + name: string; + symbol: string; + decimals: number; + }; + blockExplorerUrl: string; +} + +export interface ChainConfig { + chain: Chain; + publicClient: PublicClient; + walletClient?: WalletClient; +} + +// Action parameters +export interface TransferParams { + fromChain: SupportedChain; + toAddress: Address; + amount: string; + data?: `0x${string}`; +} + +// Plugin configuration +export interface ArtheraPluginConfig { + rpcUrl?: { + arthera?: string; + }; + secrets?: { + ARTHERA_PRIVATE_KEY: string; + }; + testMode?: boolean; + multicall?: { + batchSize?: number; + wait?: number; + }; +} + +export interface ProviderError extends Error { + code?: number; + data?: unknown; +} diff --git a/packages/plugin-arthera/tsconfig.json b/packages/plugin-arthera/tsconfig.json new file mode 100644 index 0000000000..b6ce190d98 --- /dev/null +++ b/packages/plugin-arthera/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "typeRoots": [ + "./node_modules/@types", + "./src/types" + ], + "declaration": true + }, + "include": [ + "src" + ] +} diff --git a/packages/plugin-arthera/tsup.config.ts b/packages/plugin-arthera/tsup.config.ts new file mode 100644 index 0000000000..04abb28556 --- /dev/null +++ b/packages/plugin-arthera/tsup.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "viem", + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f241d42c7f..231b5bd4c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -156,6 +156,9 @@ importers: '@elizaos/plugin-avail': specifier: workspace:* version: link:../packages/plugin-avail + '@elizaos/plugin-arthera': + specifier: workspace:* + version: link:../packages/plugin-arthera '@elizaos/plugin-avalanche': specifier: workspace:* version: link:../packages/plugin-avalanche @@ -1058,6 +1061,22 @@ importers: specifier: 7.1.0 version: 7.1.0 + packages/plugin-arthera: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.1) + viem: + specifier: 2.21.58 + version: 2.21.58(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + devDependencies: + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + packages/plugin-avail: dependencies: '@elizaos/core': From 5995cb760d59066b53aa9fb4bb3db60dd3bb887a Mon Sep 17 00:00:00 2001 From: Bertrand Juglas Date: Mon, 6 Jan 2025 00:11:44 +0100 Subject: [PATCH 30/58] chore: bump version to 0.1.8-alpha.1 for plugin-arthera --- packages/plugin-arthera/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-arthera/package.json b/packages/plugin-arthera/package.json index 3431ecf67e..db58990809 100644 --- a/packages/plugin-arthera/package.json +++ b/packages/plugin-arthera/package.json @@ -1,6 +1,6 @@ { "name": "@elizaos/plugin-arthera", - "version": "0.1.7-alpha.2", + "version": "0.1.8-alpha.1", "main": "dist/index.js", "type": "module", "types": "dist/index.d.ts", From 6a26c70e0152028524dffcaf38b34662fc125294 Mon Sep 17 00:00:00 2001 From: Phlo Date: Sun, 5 Jan 2025 19:43:02 -0600 Subject: [PATCH 31/58] docs: Re-fix timestamp links in WDYGDTW 4 notes --- docs/community/Streams/12-2024/2024-12-06.md | 132 +++++++------------ 1 file changed, 47 insertions(+), 85 deletions(-) diff --git a/docs/community/Streams/12-2024/2024-12-06.md b/docs/community/Streams/12-2024/2024-12-06.md index 2ef14a53b7..48bb313c6a 100644 --- a/docs/community/Streams/12-2024/2024-12-06.md +++ b/docs/community/Streams/12-2024/2024-12-06.md @@ -8,95 +8,56 @@ description: "Communications, Updates and Accountability" **Communications, Updates and Accountability** -Date: 2024-12-06 -Twitter Spaces: https://x.com/i/spaces/1lDxLlryWXaxm -YouTube Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4 +- Date: 2024-12-06 +- Twitter Spaces: https://x.com/i/spaces/1lDxLlryWXaxm +- YouTube Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4 -## Timestamps - -**00:01:09** - Meeting start, expectations (5-minute updates, focus on this week's achievements). - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=69 - -**00:02:50** - Shaw's update (dev school, in-person meetup). - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=170 - -**00:04:59** - Project growth, coordination challenges, need for AI project management tools. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=299 - -**00:09:22** - Call for contributors to speak, starting with Reality Spiral. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=562 - -**00:10:04** - Reality Spiral: Github integration, testing framework, Coinbase work. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=604 - -**00:17:13** - Boyaloxer: Plugin Feel (emotional adjustments for agents). - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1033 - -**00:18:37** - Spaceodili: Discord growth, summarization systems. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1117 - -**00:19:33** - Yodamaster726: Using agents in university classes, championing Llama. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1173 - -**00:23:32** - Wiki: Suggestion for a project newsletter. Discussion about contributor summarization. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1412 - -**00:26:00** - Hashwarlock: Remote Attestation Explorer upgrades, Reddit client, TEE as a service. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1560 - -**00:28:45** - KyleSt4rgarden: Eliza Framework Council, focus on stability and unified messaging bus. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=1725 - -**00:33:22** - Nasdao\_: Self-sustaining AI DAO, AI agent running validator. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2002 - -**00:34:57** - Evepredict: Slack integration, Reddit client/search, text/video to video project. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2097 - -**00:44:02** - ByornOeste: Dark Sun project launch, uncensored agent, video generator. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2642 - -**00:47:37** - Empyrealdev: LayerZero integrations, Python tooling for Solana. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=2857 - -**00:52:16** - SkotiVi: Question about ai16z bot tech stack (it's Eliza). - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3136 - -**00:54:19** - YoungBalla1000x: 15-year-old builder, project update, wallet drained. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3259 - -**00:56:47** - SOL_CryptoGamer: Cizem’s PFP collection launch and success. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3407 - -**01:02:17** - Angelocass: Experimenting with agents, excited about the potential. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3737 - -**01:03:15** - DAOJonesPumpAI: Spam bot detection, FAL API PR, Solana wallet prototype. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3795 -**01:06:38** - RodrigoSotoAlt: 3D NFTs for Bosu, 3D portal, using latest Eliza version. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=3998 - -**01:10:43** - cryptocomix1: Job interviews, learning about AI agents, interested in 3D design. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4243 - -**01:13:54** - TheBigOneGG: ERC20/SPL integration in game, ai16z cosmetic items. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4434 - -**01:15:18** - Louround\_: Thales project update, data sources, MPC wallet plugin. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4518 - -**01:22:59** - btspoony: Flow blockchain integration PR merged, multi-account control. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=4979 - -**01:25:48** - 0xamericanspiri: Goldman Stanley DAO launch on daos.fun, using hyperliquid airdrop. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5148 - -**01:28:24** - Hawkeye_Picks: Experimenting with Degen Spartan AI, exploring AI in collectibles. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5304 - -**01:36:33** - BV_Bloom1: Live video chat plugin modifications, integrating conversation models into 3D environment. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5793 - -**01:39:44** - pawgDAO: Gamified governance experiments, using Cursor, integrating AI16z. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=5984 - -**01:43:24** - jpegyguggenheim: Artist interested in AI, exploring dev school. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6204 - -**01:44:07** - heathenft: Super Swarm DevNet launch on fxn. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6247 - -**01:46:28** - Roberto9211999: (Brief interruption) Grok discussion. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6388 - -**01:48:18** - godfreymeyer: Unity scaffolding for 3D AI TV project. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6498 - -**01:51:16** - Victor28612594: Fungo team building AlphaScan agent, data enrichment plugin. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6676 - -**01:53:23** - SidDegen: OnlyCalls launch, data pipeline, beta release plans. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6803 - -**01:55:00** - O_on_X: Ico onboarding, 2D video models, comfyUI for art. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=6900 - -**02:01:00** - yikesawjeez: Memecoin cleanup crew, github.io profiles, security team, screenpipe/supabase. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7260 - -**02:05:31** - TrenchBuddy: Launching AI agent, working on EC2 and authorization. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7531 - -**02:09:49** - TSSnft: Sneakerhead Society introduction, exploring AI agent solutions. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7789 - -**02:11:40** - SidDegen: Question about the future of AI agents. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=7900 +## Timestamps -**02:16:15** - GoatOfGamblers: Building a permissionless polymarket for memecoins. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=8175 +- [00:01:09]() - Meeting start, expectations (5-minute updates, focus on this week's achievements). +- [00:02:50]() - Shaw's update (dev school, in-person meetup). +- [00:04:59]() - Project growth, coordination challenges, need for AI project management tools. +- [00:09:22]() - Call for contributors to speak, starting with Reality Spiral. +- [00:10:04]() - **Reality Spiral**: Github integration, testing framework, Coinbase work. +- [00:17:13]() - **Boyaloxer**: Plugin Feel (emotional adjustments for agents). +- [00:18:37]() - **Spaceodili**: Discord growth, summarization systems. +- [00:19:33]() - **Yodamaster726**: Using agents in university classes, championing Llama. +- [00:23:32]() - **Wiki**: Suggestion for a project newsletter. Discussion about contributor summarization. +- [00:26:00]() - **Hashwarlock**: Remote Attestation Explorer upgrades, Reddit client, TEE as a service. +- [00:28:45]() - **KyleSt4rgarden**: Eliza Framework Council, focus on stability and unified messaging bus. +- [00:33:22]() - **Nasdao_**: Self-sustaining AI DAO, AI agent running validator. +- [00:34:57]() - **Evepredict**: Slack integration, Reddit client/search, text/video to video project. +- [00:44:02]() - **ByornOeste**: Dark Sun project launch, uncensored agent, video generator. +- [00:47:37]() - **Empyrealdev**: LayerZero integrations, Python tooling for Solana. +- [00:52:16]() - **SkotiVi**: Question about ai16z bot tech stack (it's Eliza). +- [00:54:19]() - **YoungBalla1000x**: 15-year-old builder, project update, wallet drained. +- [00:56:47]() - **SOL_CryptoGamer**: Cizem's PFP collection launch and success. +- [01:02:17]() - **Angelocass**: Experimenting with agents, excited about the potential. +- [01:03:15]() - **DAOJonesPumpAI**: Spam bot detection, FAL API PR, Solana wallet prototype. +- [01:06:38]() - **RodrigoSotoAlt**: 3D NFTs for Bosu, 3D portal, using latest Eliza version. +- [01:10:43]() - **cryptocomix1**: Job interviews, learning about AI agents, interested in 3D design. +- [01:13:54]() - **TheBigOneGG**: ERC20/SPL integration in game, ai16z cosmetic items. +- [01:15:18]() - **Louround_**: Thales project update, data sources, MPC wallet plugin. +- [01:22:59]() - **btspoony**: Flow blockchain integration PR merged, multi-account control. +- [01:25:48]() - **0xamericanspiri**: Goldman Stanley DAO launch on daos.fun, using hyperliquid airdrop. +- [01:28:24]() - **Hawkeye_Picks**: Experimenting with Degen Spartan AI, exploring AI in collectibles. +- [01:36:33]() - **BV_Bloom1**: Live video chat plugin modifications, integrating conversation models into 3D environment. +- [01:39:44]() - **pawgDAO**: Gamified governance experiments, using Cursor, integrating AI16z. +- [01:43:24]() - **jpegyguggenheim**: Artist interested in AI, exploring dev school. +- [01:44:07]() - **heathenft**: Super Swarm DevNet launch on fxn. +- [01:46:28]() - **Roberto9211999**: (Brief interruption) Grok discussion. +- [01:48:18]() - **godfreymeyer**: Unity scaffolding for 3D AI TV project. +- [01:51:16]() - **Victor28612594**: Fungo team building AlphaScan agent, data enrichment plugin. +- [01:53:23]() - **SidDegen**: OnlyCalls launch, data pipeline, beta release plans. +- [01:55:00]() - **O_on_X**: Ico onboarding, 2D video models, comfyUI for art. +- [02:01:00]() - **yikesawjeez**: Memecoin cleanup crew, github.io profiles, security team, screenpipe/supabase. +- [02:05:31]() - **TrenchBuddy**: Launching AI agent, working on EC2 and authorization. +- [02:09:49]() - **TSSnft**: Sneakerhead Society introduction, exploring AI agent solutions. +- [02:11:40]() - **SidDegen**: Question about the future of AI agents. +- [02:16:15]() - **GoatOfGamblers**: Building a permissionless polymarket for memecoins. +- [02:18:01]() - Shaw's closing remarks, focus on stability and applications, call for contributions. -**02:18:01** - Shaw's closing remarks, focus on stability and applications, call for contributions. - Link: https://www.youtube.com/watch?v=r3Z4lvu_ic4&t=8281 ## Summary @@ -118,6 +79,7 @@ The fourth weekly ai16z meeting, hosted by Shaw, focused on accountability and s Overall, the meeting conveyed a sense of rapid progress, excitement, and a strong community spirit driving the Eliza project forward. + ## Hot Takes 1. **"But they're really fucking cool. Really, really, really cool stuff...you're going to have to see it on the timeline when it drops." (00:03:43)** - Shaw teases secret projects with strong conviction, building anticipation and hype, but offering zero specifics. This generates buzz but can also frustrate listeners wanting concrete info. From 6dc4144aa5970be91786335418c6a9c8c0648d16 Mon Sep 17 00:00:00 2001 From: Phlo Date: Sun, 5 Jan 2025 20:03:24 -0600 Subject: [PATCH 32/58] docs: Fix timestamp links for Dev School 3 --- docs/community/Streams/12-2024/2024-12-05.md | 42 ++++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/docs/community/Streams/12-2024/2024-12-05.md b/docs/community/Streams/12-2024/2024-12-05.md index e425c8a323..53bab8a3d9 100644 --- a/docs/community/Streams/12-2024/2024-12-05.md +++ b/docs/community/Streams/12-2024/2024-12-05.md @@ -8,71 +8,60 @@ description: "Form-Filling Frenzy & Eliza's Wild Ride" **Form-Filling Frenzy & Eliza's Wild Ride** -Date: 2024-12-05 -YouTube Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU +- Date: 2024-12-05 +- YouTube Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU + ## Timestamps -**00:00:00** - Intro & Housekeeping: +[00:00:00]() - Intro & Housekeeping: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=0 - Recap of previous sessions (Typescript, plugins, actions) - Importance of staying on the latest Eliza branch - How to pull latest changes and stash local modifications -**00:08:05** - Building a Form-Filling Agent: +[00:08:05]() - Building a Form-Filling Agent: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=485 - Introduction to Providers & Evaluators - Practical use case: Extracting user data (name, location, job) - Steps for a provider-evaluator loop to gather info and trigger actions -**00:16:15** - Deep Dive into Evaluators: +[00:16:15]() - Deep Dive into Evaluators: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=975 - Understanding "Evaluator" in Eliza's context - When they run, their role in agent's self-reflection -**00:27:45** - Code walkthrough of the "Fact Evaluator": - -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=1675 -- Code walkthrough of the "Fact Evaluator" +[00:27:45]() - Code walkthrough of the "Fact Evaluator" -**00:36:07** - Building a User Data Evaluator: +[00:36:07]() - Building a User Data Evaluator: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=2167 - Starting from scratch, creating a basic evaluator - Registering the evaluator directly in the agent (no plugin) - Logging evaluator activity and inspecting context -**00:51:50** - Exploring Eliza's Cache Manager: +[00:51:50]() - Exploring Eliza's Cache Manager: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3110 - Shaw uses Code2Prompt to analyze cache manager code - Applying cache manager principles to user data storage -**01:06:01** - Using Claude AI for Code Generation: +[01:06:01]() - Using Claude AI for Code Generation: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=3961 - Pasting code into Claude and giving instructions - Iterative process: Refining code and providing feedback to Claude -**01:21:18** - Testing the User Data Flow: +[01:21:18]() - Testing the User Data Flow: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=4878 - Running the agent and interacting with it - Observing evaluator logs and context injections - Troubleshooting and iterating on code based on agent behavior -**01:30:27** - Adding a Dynamic Provider Based on Completion: +[01:30:27]() - Adding a Dynamic Provider Based on Completion: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5427 - Creating a new provider that only triggers after user data is collected - Example: Providing a secret code or access link as a reward -**01:37:16** - Q&A with the Audience: +[01:37:16]() - Q&A with the Audience: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=5836 - Python vs. TypeScript agents - Pre-evaluation vs. post-evaluation hooks - Agent overwhelm with many plugins/evaluators @@ -80,12 +69,12 @@ YouTube Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU - Running stateless agents - Building AIXBT agents -**01:47:31** - Outro and Next Steps: +[01:47:31]() - Outro and Next Steps: -- Link: https://www.youtube.com/watch?v=Y1DiqSVy4aU&t=6451 - Recap of key learnings and the potential of provider-evaluator loops - Call to action: Share project ideas and feedback for future sessions + ## Summary This is the third part of the live stream series "AI Agent Dev School" hosted by Shaw from ai16z, focusing on building AI agents using the Eliza framework. @@ -102,6 +91,7 @@ This is the third part of the live stream series "AI Agent Dev School" hosted by **Overall, this live stream provided a practical tutorial on building a common AI agent use case (form filling) while emphasizing the potential of the Eliza framework for developing a wide range of agentic applications.** + ## Hot Takes 1. **"I'm just going to struggle bus some code today." (00:09:31,664)** - Shaw embraces a "struggle bus" approach, showcasing live coding with errors and debugging, reflecting the reality of AI agent development. This contrasts with polished tutorials, highlighting the iterative and messy nature of this new technology. From 150a45faf0b1773f0ce75e6dc61da93349795061 Mon Sep 17 00:00:00 2001 From: Phlo Date: Sun, 5 Jan 2025 20:27:37 -0600 Subject: [PATCH 33/58] docs: Fix timestamp links for Dev School 4 --- docs/community/Streams/12-2024/2024-12-10.md | 45 +++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/community/Streams/12-2024/2024-12-10.md b/docs/community/Streams/12-2024/2024-12-10.md index ab6258e163..0cee7d40e4 100644 --- a/docs/community/Streams/12-2024/2024-12-10.md +++ b/docs/community/Streams/12-2024/2024-12-10.md @@ -8,35 +8,37 @@ description: "AI Pizza: Hacking Eliza for Domino's Delivery (plus TEE Deep Dive) **AI Pizza: Hacking Eliza for Domino's Delivery (plus TEE Deep Dive)** -Date: 2024-12-10 -YouTube Link: https://www.youtube.com/watch?v=6I9e9pJprDI +- Date: 2024-12-10 +- YouTube Link: https://www.youtube.com/watch?v=6I9e9pJprDI + ## Timestamps Part 1: Trusted Execution Environments (TEEs) with Agent Joshua -- **00:00:09** - Stream starts, initial setup issues. -- **00:01:58** - Intro to Trusted Execution Environments (TEEs). -- **00:08:03** - Agent Joshua begins explaining TEEs and the Eliza plugin. -- **00:19:15** - Deeper dive into remote attestation. -- **00:24:50** - Discussion of derived keys. -- **00:37:00** - Deploying to a real TEE, Phala Network's TEE cloud. -- **00:50:48** - Q&A with Joshua, contact info, and next steps. +- [00:00:09]() - Stream starts, initial setup issues. +- [00:01:58]() - Intro to Trusted Execution Environments (TEEs). +- [00:08:03]() - Agent Joshua begins explaining TEEs and the Eliza plugin. +- [00:19:15]() - Deeper dive into remote attestation. +- [00:24:50]() - Discussion of derived keys. +- [00:37:00]() - Deploying to a real TEE, Phala Network's TEE cloud. +- [00:50:48]() - Q&A with Joshua, contact info, and next steps. Part 2: Building a Domino's pizza ordering agent -- **01:04:37** - Transition to building a Domino's pizza ordering agent. -- **01:14:20** - Discussion of the pizza ordering agent’s order flow and state machine. -- **01:22:07** - Using Claude to generate a state machine diagram. -- **01:32:17** - Creating the Domino's plugin in Eliza. -- **01:54:15** - Working on the pizza order provider. -- **02:16:46** - Pizza provider code completed. -- **02:28:50** - Discussion of caching customer and order data. -- **03:13:45** - Pushing fixes to main branch and continuing work on the agent. -- **04:24:30** - Discussion of summarizing past agent dev school sessions. -- **05:01:18** - Shaw returns, admits to ordering Domino's manually. -- **05:09:00** - Discussing payment flow and a confirm order action. -- **05:27:17** - Final code push, wrap-up, and end of stream. +- [01:04:37]() - Transition to building a Domino's pizza ordering agent. +- [01:14:20]() - Discussion of the pizza ordering agent’s order flow and state machine. +- [01:22:07]() - Using Claude to generate a state machine diagram. +- [01:32:17]() - Creating the Domino's plugin in Eliza. +- [01:54:15]() - Working on the pizza order provider. +- [02:16:46]() - Pizza provider code completed. +- [02:28:50]() - Discussion of caching customer and order data. +- [03:13:45]() - Pushing fixes to main branch and continuing work on the agent. +- [04:24:30]() - Discussion of summarizing past agent dev school sessions. +- [05:01:18]() - Shaw returns, admits to ordering Domino's manually. +- [05:09:00]() - Discussing payment flow and a confirm order action. +- [05:27:17]() - Final code push, wrap-up, and end of stream. + ## Summary @@ -86,6 +88,7 @@ In the second part, Shaw transitions to a more lighthearted coding session where - He uses Claude (an AI assistant) to generate code snippets and assist with the development process. - He decides to initially focus on a simplified version where the user's payment information is hardcoded in the environment variables, and the agent only needs to collect the user's address. + ## Hot Takes 1. **"Maybe we'll mix it on LinkedIn so people can order Domino's on LinkedIn. There you go. Now we're cooking." (00:03:26)** - Shaw's seemingly flippant idea of ordering pizza on LinkedIn highlights the potential for integrating everyday services into unexpected platforms through agents. This sparked discussion about the wider implications for businesses and social media. From 287abd960e0af5274d4a1f6004e0e461a0ed1589 Mon Sep 17 00:00:00 2001 From: Phlo Date: Sun, 5 Jan 2025 20:48:19 -0600 Subject: [PATCH 34/58] docs: Re-fix timestamp links in WDYGDTW 5 notes --- docs/community/Streams/12-2024/2024-12-13.md | 120 +++++++------------ 1 file changed, 43 insertions(+), 77 deletions(-) diff --git a/docs/community/Streams/12-2024/2024-12-13.md b/docs/community/Streams/12-2024/2024-12-13.md index bda0b80d64..443de2e2e4 100644 --- a/docs/community/Streams/12-2024/2024-12-13.md +++ b/docs/community/Streams/12-2024/2024-12-13.md @@ -8,86 +8,51 @@ description: "Building the Future: 30+ Developers Share Their AI Agent Progress" **Building the Future: 30+ Developers Share Their AI Agent Progress** -Date: 2024-12-13 -Twitter Spaces: https://x.com/i/spaces/1lDxLlgYjMkxm -YouTube Link: https://www.youtube.com/watch?v=4u8rbjmvWC0 +- Date: 2024-12-13 +- Twitter Spaces: https://x.com/i/spaces/1lDxLlgYjMkxm +- YouTube Link: https://www.youtube.com/watch?v=4u8rbjmvWC0 + ## Timestamps -- **00:01:04** - shawmakesmagic: Introduction and Format Changes for the Space - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=64 -- **00:02:38** - xsubtropic: Redux project, DaVinci AI - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=158 -- **00:06:57** - CottenIO: Scripted, AI Summit Recap - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=417 -- **00:08:58** - HDPbilly: Real Agency HQ, "Sploot" agent - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=538 -- **00:13:29** - IQ6900: On-chain ASCII art service - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=809 -- **00:18:50** - frankdegods: Eliza Character Sheet Tweaks - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1130 -- **00:20:15** - jamesyoung: AI Agent Starter Kit - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1215 -- **00:23:29** - 0xglu: Ducky and Agent Swarms - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1409 -- **00:25:30** - chrislatorres: Eliza.gg - Eliza documentation site - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1530 -- **00:27:47** - reality_spiral: Self-Improving Agents & Github integration - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1667 -- **00:31:43** - robotsreview: Story Protocol plugin and Agentic TCPIP - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=1903 -- **00:34:19** - shannonNullCode: Emblem Vault & Message Ingestion - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=2059 -- **00:38:40** - bcsmithx: Agent Tank - Computer use agents - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=2320 -- **00:41:20** - boyaloxer: Plugin Feel - Emotion-based agent - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=2480 -- **00:44:09** - JustJamieJoyce: Muse of Truth/Research AI agents - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=2649 -- **00:46:11** - yikesawjeez: Discord bot & Contribution updates - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=2771 -- **00:50:56** - RodrigoSotoAlt: Monad, Metaplex Nfts, Solana integrations - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3056 -- **00:53:22** - HowieDuhzit: Eliza Character Generator - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3202 -- **00:55:57** - xrpublisher: XR Publisher, 3D Social Network on the edge - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3357 -- **01:00:57** - BV_Bloom1: 3D Agent Interactions - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3657 -- **01:02:57** - nftRanch: Trading Bot and Eliza V2 integrations - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3777 -- **01:05:57** - 019ec6e2: Mimetic Platform and Agent Interactions - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=3957 -- **01:09:17** - jacobmtucker: Agent Transaction Control Protocol - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4157 -- **01:12:26** - CurtisLaird5: C-Studio character interface - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4346 -- **01:17:13** - unl\_\_cky: Escapism, art generation AI - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4633 -- **01:19:17** - Rowdymode: Twin Tone - Interactive Streaming - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4757 -- **01:20:29** - mitchcastanet: Binary Star System research with agents - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4829 -- **01:23:15** - GoatOfGamblers: Prediction market for meme coins - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=4995 -- **01:25:27** - JohnNaulty: SWE contributions, plugin working groups - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=5127 -- **01:29:30** - mayanicks0x: Axie, AI KOL Agent - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=5370 -- **01:31:30** - wakesync: Eliza Wakes Up, web app updates - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=5490 -- **01:35:28** - TrenchBuddy: Trading agents and AWS templates - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=5728 -- **01:38:36** - rakshitaphilip: Brunette token and agent tips on Warpcast - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=5916 -- **01:44:49** - MbBrainz: Menu Recommendation app - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=6289 -- **01:46:03** - Hawkeye_Picks: Storytelling bot - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=6363 -- **01:49:16** - shawmakesmagic: Hiring and Eliza V2 - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=6556 -- **01:54:30** - dankvr: Community updates, tooling - - Link: https://www.youtube.com/watch?v=4u8rbjmvWC0&t=6870 +- [00:01:04]() - **shawmakesmagic**: Introduction and Format Changes for the Space +- [00:02:38]() - **xsubtropic**: Redux project, DaVinci AI +- [00:06:57]() - **CottenIO**: Scripted, AI Summit Recap +- [00:08:58]() - **HDPbilly**: Real Agency HQ, "Sploot" agent +- [00:13:29]() - **IQ6900**: On-chain ASCII art service +- [00:18:50]() - **frankdegods**: Eliza Character Sheet Tweaks +- [00:20:15]() - **jamesyoung**: AI Agent Starter Kit +- [00:23:29]() - **0xglu**: Ducky and Agent Swarms +- [00:25:30]() - **chrislatorres**: Eliza.gg - Eliza documentation site +- [00:27:47]() - **reality_spiral**: Self-Improving Agents & Github integration +- [00:31:43]() - **robotsreview**: Story Protocol plugin and Agentic TCPIP +- [00:34:19]() - **shannonNullCode**: Emblem Vault & Message Ingestion +- [00:38:40]() - **bcsmithx**: Agent Tank - Computer use agents +- [00:41:20]() - **boyaloxer**: Plugin Feel - Emotion-based agent +- [00:44:09]() - **JustJamieJoyce**: Muse of Truth/Research AI agents +- [00:46:11]() - **yikesawjeez**: Discord bot & Contribution updates +- [00:50:56]() - **RodrigoSotoAlt**: Monad, Metaplex Nfts, Solana integrations +- [00:53:22]() - **HowieDuhzit**: Eliza Character Generator +- [00:55:57]() - **xrpublisher**: XR Publisher, 3D Social Network on the edge +- [01:00:57]() - **BV_Bloom1**: 3D Agent Interactions +- [01:02:57]() - **nftRanch**: Trading Bot and Eliza V2 integrations +- [01:05:57]() - **019ec6e2**: Mimetic Platform and Agent Interactions +- [01:09:17]() - **jacobmtucker**: Agent Transaction Control Protocol +- [01:12:26]() - **CurtisLaird5**: C-Studio character interface +- [01:17:13]() - **unl__cky**: Escapism, art generation AI +- [01:19:17]() - **Rowdymode**: Twin Tone - Interactive Streaming +- [01:20:29]() - **mitchcastanet**: Binary Star System research with agents +- [01:23:15]() - **GoatOfGamblers**: Prediction market for meme coins +- [01:25:27]() - **JohnNaulty**: SWE contributions, plugin working groups +- [01:29:30]() - **mayanicks0x**: Axie, AI KOL Agent +- [01:31:30]() - **wakesync**: Eliza Wakes Up, web app updates +- [01:35:28]() - **TrenchBuddy**: Trading agents and AWS templates +- [01:38:36]() - **rakshitaphilip**: Brunette token and agent tips on Warpcast +- [01:44:49]() - **MbBrainz**: Menu Recommendation app +- [01:46:03]() - **Hawkeye_Picks**: Storytelling bot +- [01:49:16]() - **shawmakesmagic**: Hiring and Eliza V2 +- [01:54:30]() - **dankvr**: Community updates, tooling + ## Summary @@ -115,6 +80,7 @@ This Twitter Spaces event, hosted by ai16z and titled "What Did You Get Done Thi Overall, this event showed a vibrant and active community rapidly developing projects using the Eliza framework. It highlighted both the significant progress made in the past week and the challenges being tackled, showcasing the potential for AI agents in diverse real world applications. + ## Hot Takes 1. **"These corporations are going to cease to exist."** - **(00:07:31)** Tim Cotton makes a bold prediction about the future of traditional corporations in the face of AI agent technology. This implies a near-term and disruptive shift. From 9f0cc2803379b770f2e50d8ab5667c94d6617ce7 Mon Sep 17 00:00:00 2001 From: beekeeper Date: Mon, 6 Jan 2025 20:26:06 +0800 Subject: [PATCH 35/58] Update index.ts --- packages/plugin-goplus/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-goplus/src/index.ts b/packages/plugin-goplus/src/index.ts index d64fb3e905..3af4f75627 100644 --- a/packages/plugin-goplus/src/index.ts +++ b/packages/plugin-goplus/src/index.ts @@ -7,7 +7,7 @@ export * from "./services/GoplusSecurityService"; export const goplusPlugin: Plugin = { name: "goplus", description: - "goplus Plugin for Eliza - Enables WebSocket communication for AI-driven market insights", + "goplus Plugin for Eliza - Enables on-chain security checks", actions: [], evaluators: [], providers: [], From e58006670547126dfe9db1e5f1427240deaf2ca8 Mon Sep 17 00:00:00 2001 From: zhourunlai Date: Tue, 7 Jan 2025 00:40:58 +0800 Subject: [PATCH 36/58] fix empty input text --- packages/client-direct/src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/client-direct/src/index.ts b/packages/client-direct/src/index.ts index 8f7f224b00..561365d60f 100644 --- a/packages/client-direct/src/index.ts +++ b/packages/client-direct/src/index.ts @@ -226,6 +226,12 @@ export class DirectClient { ); const text = req.body.text; + // if empty text, directly return + if (!text) { + res.json([]); + return; + } + const messageId = stringToUuid(Date.now().toString()); const attachments: Media[] = []; From a6be0d1f478fbec1e88efa0090609ffe1392ea2d Mon Sep 17 00:00:00 2001 From: CODe <71spates.bravest@icloud.com> Date: Mon, 6 Jan 2025 19:35:57 +0100 Subject: [PATCH 37/58] Fix typo Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4646b5f74a..ecf1ba6e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1168,7 +1168,7 @@ - Abstract transcript provider [\#73](https://github.com/elizaOS/eliza/issues/73) - 🤖 Confidence Level Implementation [\#50](https://github.com/elizaOS/eliza/issues/50) - 📈 Trading Assistant Implementation [\#48](https://github.com/elizaOS/eliza/issues/48) -- swap Dao action initital [\#196](https://github.com/elizaOS/eliza/pull/196) ([MarcoMandar](https://github.com/MarcoMandar)) +- swap Dao action initial [\#196](https://github.com/elizaOS/eliza/pull/196) ([MarcoMandar](https://github.com/MarcoMandar)) **Fixed bugs:** From 8846b44b6c0f647c9b4f1088249a8dff22047418 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 6 Jan 2025 20:44:46 +0100 Subject: [PATCH 38/58] upd: hide character settings secrets from api route --- packages/client-direct/src/api.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/client-direct/src/api.ts b/packages/client-direct/src/api.ts index 6d5ac569f5..45f3eed58e 100644 --- a/packages/client-direct/src/api.ts +++ b/packages/client-direct/src/api.ts @@ -54,6 +54,11 @@ export function createApiRouter( return; } + let character = agent?.character; + if (character?.settings?.secrets) { + delete character.settings.secrets; + } + res.json({ id: agent.agentId, character: agent.character, From bf30c2cd8f256793fcbe25e2e36d1f1334d89cb3 Mon Sep 17 00:00:00 2001 From: ai16z-demirix Date: Mon, 6 Jan 2025 21:11:11 +0100 Subject: [PATCH 39/58] test: adding tests for discord-client --- .../__tests__/discord-client.test.ts | 111 ++++++++++++++++++ packages/client-discord/package.json | 6 +- packages/client-discord/vitest.config.ts | 14 +++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 packages/client-discord/__tests__/discord-client.test.ts create mode 100644 packages/client-discord/vitest.config.ts diff --git a/packages/client-discord/__tests__/discord-client.test.ts b/packages/client-discord/__tests__/discord-client.test.ts new file mode 100644 index 0000000000..969e049187 --- /dev/null +++ b/packages/client-discord/__tests__/discord-client.test.ts @@ -0,0 +1,111 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { DiscordClient } from '../src'; +import { Client, Events } from 'discord.js'; + +// Mock @elizaos/core +vi.mock('@elizaos/core', () => ({ + elizaLogger: { + info: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + }, + getEmbeddingZeroVector: () => new Array(1536).fill(0), + stringToUuid: (str: string) => str, + messageCompletionFooter: '# INSTRUCTIONS: Choose the best response for the agent.', + shouldRespondFooter: '# INSTRUCTIONS: Choose if the agent should respond.', + generateMessageResponse: vi.fn(), + generateShouldRespond: vi.fn(), + composeContext: vi.fn(), + composeRandomUser: vi.fn(), +})); + +// Mock discord.js Client +vi.mock('discord.js', () => { + const mockClient = { + login: vi.fn().mockResolvedValue('token'), + on: vi.fn(), + once: vi.fn(), + destroy: vi.fn().mockResolvedValue(undefined), + }; + + return { + Client: vi.fn(() => mockClient), + Events: { + ClientReady: 'ready', + MessageCreate: 'messageCreate', + VoiceStateUpdate: 'voiceStateUpdate', + MessageReactionAdd: 'messageReactionAdd', + MessageReactionRemove: 'messageReactionRemove', + }, + GatewayIntentBits: { + Guilds: 1, + DirectMessages: 2, + GuildVoiceStates: 3, + MessageContent: 4, + GuildMessages: 5, + DirectMessageTyping: 6, + GuildMessageTyping: 7, + GuildMessageReactions: 8, + }, + Partials: { + Channel: 'channel', + Message: 'message', + User: 'user', + Reaction: 'reaction', + }, + Collection: class Collection extends Map {}, + }; +}); + +describe('DiscordClient', () => { + let mockRuntime: any; + let discordClient: DiscordClient; + + beforeEach(() => { + mockRuntime = { + getSetting: vi.fn((key: string) => { + if (key === 'DISCORD_API_TOKEN') return 'mock-token'; + return undefined; + }), + getState: vi.fn(), + setState: vi.fn(), + getMemory: vi.fn(), + setMemory: vi.fn(), + getService: vi.fn(), + registerAction: vi.fn(), + providers: [], + character: { + clientConfig: { + discord: { + shouldIgnoreBotMessages: true + } + } + } + }; + + discordClient = new DiscordClient(mockRuntime); + }); + + it('should initialize with correct configuration', () => { + expect(discordClient.apiToken).toBe('mock-token'); + expect(discordClient.client).toBeDefined(); + expect(mockRuntime.getSetting).toHaveBeenCalledWith('DISCORD_API_TOKEN'); + }); + + it('should login to Discord on initialization', () => { + expect(discordClient.client.login).toHaveBeenCalledWith('mock-token'); + }); + + it('should register event handlers on initialization', () => { + expect(discordClient.client.once).toHaveBeenCalledWith(Events.ClientReady, expect.any(Function)); + expect(discordClient.client.on).toHaveBeenCalledWith('guildCreate', expect.any(Function)); + expect(discordClient.client.on).toHaveBeenCalledWith(Events.MessageReactionAdd, expect.any(Function)); + expect(discordClient.client.on).toHaveBeenCalledWith(Events.MessageReactionRemove, expect.any(Function)); + expect(discordClient.client.on).toHaveBeenCalledWith('voiceStateUpdate', expect.any(Function)); + }); + + it('should clean up resources when stopped', async () => { + await discordClient.stop(); + expect(discordClient.client.destroy).toHaveBeenCalled(); + }); +}); diff --git a/packages/client-discord/package.json b/packages/client-discord/package.json index 795788be02..49f7ac89e5 100644 --- a/packages/client-discord/package.json +++ b/packages/client-discord/package.json @@ -30,12 +30,14 @@ "zod": "3.23.8" }, "devDependencies": { - "tsup": "8.3.5" + "tsup": "8.3.5", + "vitest": "1.2.1" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "lint": "eslint --fix --cache ." + "lint": "eslint --fix --cache .", + "test": "vitest run" }, "trustedDependencies": { "@discordjs/opus": "github:discordjs/opus", diff --git a/packages/client-discord/vitest.config.ts b/packages/client-discord/vitest.config.ts new file mode 100644 index 0000000000..a11fbbd0d9 --- /dev/null +++ b/packages/client-discord/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config'; +import { resolve } from 'path'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + }, + resolve: { + alias: { + '@elizaos/core': resolve(__dirname, '../core/src'), + }, + }, +}); From a50451c2ebdd1a2b408f254dff60e55f256c5a9d Mon Sep 17 00:00:00 2001 From: ai16z-demirix Date: Mon, 6 Jan 2025 23:57:53 +0100 Subject: [PATCH 40/58] test: adding tests for twitter-client --- .../client-twitter/__tests__/base.test.ts | 76 ++++++++++ .../__tests__/environment.test.ts | 134 ++++++++++++++++++ .../client-twitter/__tests__/post.test.ts | 97 +++++++++++++ packages/client-twitter/package.json | 8 +- packages/client-twitter/vitest.config.ts | 12 ++ 5 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 packages/client-twitter/__tests__/base.test.ts create mode 100644 packages/client-twitter/__tests__/environment.test.ts create mode 100644 packages/client-twitter/__tests__/post.test.ts create mode 100644 packages/client-twitter/vitest.config.ts diff --git a/packages/client-twitter/__tests__/base.test.ts b/packages/client-twitter/__tests__/base.test.ts new file mode 100644 index 0000000000..59a15c33c9 --- /dev/null +++ b/packages/client-twitter/__tests__/base.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ClientBase } from '../src/base'; +import { IAgentRuntime } from '@elizaos/core'; +import { TwitterConfig } from '../src/environment'; + +describe('Twitter Client Base', () => { + let mockRuntime: IAgentRuntime; + let mockConfig: TwitterConfig; + + beforeEach(() => { + mockRuntime = { + env: { + TWITTER_USERNAME: 'testuser', + TWITTER_DRY_RUN: 'true', + TWITTER_POST_INTERVAL_MIN: '5', + TWITTER_POST_INTERVAL_MAX: '10', + TWITTER_ACTION_INTERVAL: '5', + TWITTER_ENABLE_ACTION_PROCESSING: 'true', + TWITTER_POST_IMMEDIATELY: 'false', + TWITTER_SEARCH_ENABLE: 'false' + }, + getEnv: function (key: string) { + return this.env[key] || null; + }, + getSetting: function (key: string) { + return this.env[key] || null; + }, + character: { + style: { + all: ['Test style 1', 'Test style 2'], + post: ['Post style 1', 'Post style 2'] + } + } + } as unknown as IAgentRuntime; + + mockConfig = { + TWITTER_USERNAME: 'testuser', + TWITTER_DRY_RUN: true, + TWITTER_SEARCH_ENABLE: false, + TWITTER_SPACES_ENABLE: false, + TWITTER_TARGET_USERS: [], + TWITTER_MAX_TWEETS_PER_DAY: 10, + TWITTER_MAX_TWEET_LENGTH: 280, + POST_INTERVAL_MIN: 5, + POST_INTERVAL_MAX: 10, + ACTION_INTERVAL: 5, + ENABLE_ACTION_PROCESSING: true, + POST_IMMEDIATELY: false + }; + }); + + it('should create instance with correct configuration', () => { + const client = new ClientBase(mockRuntime, mockConfig); + expect(client).toBeDefined(); + expect(client.twitterConfig).toBeDefined(); + expect(client.twitterConfig.TWITTER_USERNAME).toBe('testuser'); + expect(client.twitterConfig.TWITTER_DRY_RUN).toBe(true); + }); + + it('should initialize with correct tweet length limit', () => { + const client = new ClientBase(mockRuntime, mockConfig); + expect(client.twitterConfig.TWITTER_MAX_TWEET_LENGTH).toBe(280); + }); + + it('should initialize with correct post intervals', () => { + const client = new ClientBase(mockRuntime, mockConfig); + expect(client.twitterConfig.POST_INTERVAL_MIN).toBe(5); + expect(client.twitterConfig.POST_INTERVAL_MAX).toBe(10); + }); + + it('should initialize with correct action settings', () => { + const client = new ClientBase(mockRuntime, mockConfig); + expect(client.twitterConfig.ACTION_INTERVAL).toBe(5); + expect(client.twitterConfig.ENABLE_ACTION_PROCESSING).toBe(true); + }); +}); diff --git a/packages/client-twitter/__tests__/environment.test.ts b/packages/client-twitter/__tests__/environment.test.ts new file mode 100644 index 0000000000..dccfd0584b --- /dev/null +++ b/packages/client-twitter/__tests__/environment.test.ts @@ -0,0 +1,134 @@ +import { describe, it, expect, vi } from 'vitest'; +import { validateTwitterConfig } from '../src/environment'; +import { IAgentRuntime } from '@elizaos/core'; + +describe('Twitter Environment Configuration', () => { + const mockRuntime: IAgentRuntime = { + env: { + TWITTER_USERNAME: 'testuser123', + TWITTER_DRY_RUN: 'true', + TWITTER_SEARCH_ENABLE: 'false', + TWITTER_SPACES_ENABLE: 'false', + TWITTER_TARGET_USERS: 'user1,user2,user3', + TWITTER_MAX_TWEETS_PER_DAY: '10', + TWITTER_MAX_TWEET_LENGTH: '280', + TWITTER_POST_INTERVAL_MIN: '90', + TWITTER_POST_INTERVAL_MAX: '180', + TWITTER_ACTION_INTERVAL: '5', + TWITTER_ENABLE_ACTION_PROCESSING: 'false', + TWITTER_POST_IMMEDIATELY: 'false', + TWITTER_EMAIL: 'test@example.com', + TWITTER_PASSWORD: 'hashedpassword', + TWITTER_2FA_SECRET: '', + TWITTER_POLL_INTERVAL: '120', + TWITTER_RETRY_LIMIT: '5', + ACTION_TIMELINE_TYPE: 'foryou', + MAX_ACTIONS_PROCESSING: '1', + MAX_TWEET_LENGTH: '280' + }, + getEnv: function (key: string) { + return this.env[key] || null; + }, + getSetting: function (key: string) { + return this.env[key] || null; + } + } as unknown as IAgentRuntime; + + it('should validate correct configuration', async () => { + const config = await validateTwitterConfig(mockRuntime); + expect(config).toBeDefined(); + expect(config.TWITTER_USERNAME).toBe('testuser123'); + expect(config.TWITTER_DRY_RUN).toBe(true); + expect(config.TWITTER_SEARCH_ENABLE).toBe(false); + expect(config.TWITTER_SPACES_ENABLE).toBe(false); + expect(config.TWITTER_TARGET_USERS).toEqual(['user1', 'user2', 'user3']); + expect(config.MAX_TWEET_LENGTH).toBe(280); + expect(config.POST_INTERVAL_MIN).toBe(90); + expect(config.POST_INTERVAL_MAX).toBe(180); + expect(config.ACTION_INTERVAL).toBe(5); + expect(config.ENABLE_ACTION_PROCESSING).toBe(false); + expect(config.POST_IMMEDIATELY).toBe(false); + }); + + it('should validate wildcard username', async () => { + const wildcardRuntime = { + ...mockRuntime, + env: { + ...mockRuntime.env, + TWITTER_USERNAME: '*' + }, + getEnv: function(key: string) { + return this.env[key] || null; + }, + getSetting: function(key: string) { + return this.env[key] || null; + } + } as IAgentRuntime; + + const config = await validateTwitterConfig(wildcardRuntime); + expect(config.TWITTER_USERNAME).toBe('*'); + }); + + it('should validate username with numbers and underscores', async () => { + const validRuntime = { + ...mockRuntime, + env: { + ...mockRuntime.env, + TWITTER_USERNAME: 'test_user_123' + }, + getEnv: function(key: string) { + return this.env[key] || null; + }, + getSetting: function(key: string) { + return this.env[key] || null; + } + } as IAgentRuntime; + + const config = await validateTwitterConfig(validRuntime); + expect(config.TWITTER_USERNAME).toBe('test_user_123'); + }); + + it('should handle empty target users', async () => { + const runtimeWithoutTargets = { + ...mockRuntime, + env: { + ...mockRuntime.env, + TWITTER_TARGET_USERS: '' + }, + getEnv: function(key: string) { + return this.env[key] || null; + }, + getSetting: function(key: string) { + return this.env[key] || null; + } + } as IAgentRuntime; + + const config = await validateTwitterConfig(runtimeWithoutTargets); + expect(config.TWITTER_TARGET_USERS).toHaveLength(0); + }); + + it('should use default values when optional configs are missing', async () => { + const minimalRuntime = { + env: { + TWITTER_USERNAME: 'testuser', + TWITTER_DRY_RUN: 'true', + TWITTER_EMAIL: 'test@example.com', + TWITTER_PASSWORD: 'hashedpassword', + TWITTER_2FA_SECRET: '', + MAX_TWEET_LENGTH: '280' + }, + getEnv: function (key: string) { + return this.env[key] || null; + }, + getSetting: function (key: string) { + return this.env[key] || null; + } + } as unknown as IAgentRuntime; + + const config = await validateTwitterConfig(minimalRuntime); + expect(config).toBeDefined(); + expect(config.MAX_TWEET_LENGTH).toBe(280); + expect(config.POST_INTERVAL_MIN).toBe(90); + expect(config.POST_INTERVAL_MAX).toBe(180); + }); +}); diff --git a/packages/client-twitter/__tests__/post.test.ts b/packages/client-twitter/__tests__/post.test.ts new file mode 100644 index 0000000000..7459b68d62 --- /dev/null +++ b/packages/client-twitter/__tests__/post.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect, vi } from 'vitest'; +import { TwitterPostClient } from '../src/post'; +import { ClientBase } from '../src/base'; +import { IAgentRuntime } from '@elizaos/core'; +import { TwitterConfig } from '../src/environment'; + +describe('Twitter Post Client', () => { + let mockRuntime: IAgentRuntime; + let mockConfig: TwitterConfig; + let baseClient: ClientBase; + + beforeEach(() => { + mockRuntime = { + env: { + TWITTER_USERNAME: 'testuser', + TWITTER_DRY_RUN: 'true', + TWITTER_POST_INTERVAL_MIN: '5', + TWITTER_POST_INTERVAL_MAX: '10', + TWITTER_ACTION_INTERVAL: '5', + TWITTER_ENABLE_ACTION_PROCESSING: 'true', + TWITTER_POST_IMMEDIATELY: 'false', + TWITTER_SEARCH_ENABLE: 'false', + TWITTER_EMAIL: 'test@example.com', + TWITTER_PASSWORD: 'hashedpassword', + TWITTER_2FA_SECRET: '', + TWITTER_POLL_INTERVAL: '120', + TWITTER_RETRY_LIMIT: '5', + ACTION_TIMELINE_TYPE: 'foryou', + MAX_ACTIONS_PROCESSING: '1', + MAX_TWEET_LENGTH: '280' + }, + getEnv: function (key: string) { + return this.env[key] || null; + }, + getSetting: function (key: string) { + return this.env[key] || null; + }, + character: { + style: { + all: ['Test style 1', 'Test style 2'], + post: ['Post style 1', 'Post style 2'] + } + } + } as unknown as IAgentRuntime; + + mockConfig = { + TWITTER_USERNAME: 'testuser', + TWITTER_DRY_RUN: true, + TWITTER_SEARCH_ENABLE: false, + TWITTER_SPACES_ENABLE: false, + TWITTER_TARGET_USERS: [], + TWITTER_MAX_TWEETS_PER_DAY: 10, + TWITTER_MAX_TWEET_LENGTH: 280, + POST_INTERVAL_MIN: 5, + POST_INTERVAL_MAX: 10, + ACTION_INTERVAL: 5, + ENABLE_ACTION_PROCESSING: true, + POST_IMMEDIATELY: false, + MAX_TWEET_LENGTH: 280 + }; + + baseClient = new ClientBase(mockRuntime, mockConfig); + }); + + it('should create post client instance', () => { + const postClient = new TwitterPostClient(baseClient, mockRuntime); + expect(postClient).toBeDefined(); + expect(postClient.twitterUsername).toBe('testuser'); + expect(postClient['isDryRun']).toBe(true); + }); + + it('should keep tweets under max length when already valid', () => { + const postClient = new TwitterPostClient(baseClient, mockRuntime); + const validTweet = 'This is a valid tweet'; + const result = postClient['trimTweetLength'](validTweet); + expect(result).toBe(validTweet); + expect(result.length).toBeLessThanOrEqual(280); + }); + + it('should cut at last sentence when possible', () => { + const postClient = new TwitterPostClient(baseClient, mockRuntime); + const longTweet = 'First sentence. Second sentence that is quite long. Third sentence that would make it too long.'; + const result = postClient['trimTweetLength'](longTweet); + const lastPeriod = result.lastIndexOf('.'); + expect(lastPeriod).toBeGreaterThan(0); + expect(result.length).toBeLessThanOrEqual(280); + }); + + it('should add ellipsis when cutting within a sentence', () => { + const postClient = new TwitterPostClient(baseClient, mockRuntime); + const longSentence = 'This is an extremely long sentence without any periods that needs to be truncated because it exceeds the maximum allowed length for a tweet on the Twitter platform and therefore must be shortened'; + const result = postClient['trimTweetLength'](longSentence); + const lastSpace = result.lastIndexOf(' '); + expect(lastSpace).toBeGreaterThan(0); + expect(result.length).toBeLessThanOrEqual(280); + }); +}); diff --git a/packages/client-twitter/package.json b/packages/client-twitter/package.json index 2dc3a3543e..cc66c678a2 100644 --- a/packages/client-twitter/package.json +++ b/packages/client-twitter/package.json @@ -25,12 +25,16 @@ "zod": "3.23.8" }, "devDependencies": { - "tsup": "8.3.5" + "tsup": "8.3.5", + "vitest": "1.1.3", + "@vitest/coverage-v8": "1.1.3" }, "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "lint": "eslint --fix --cache ." + "lint": "eslint --fix --cache .", + "test": "vitest run", + "test:coverage": "vitest run --coverage" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/client-twitter/vitest.config.ts b/packages/client-twitter/vitest.config.ts new file mode 100644 index 0000000000..2e60e80f5d --- /dev/null +++ b/packages/client-twitter/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['__tests__/**/*.test.ts'], + coverage: { + reporter: ['text', 'json', 'html'], + }, + }, +}); From 0c9e47c377d7473fb2b577fdc0f40ac3f8223c0f Mon Sep 17 00:00:00 2001 From: Proteus Date: Mon, 6 Jan 2025 18:44:06 -0500 Subject: [PATCH 41/58] rm unused variable x_server_url --- .env.example | 1 - README_CN.md | 3 +-- README_ES.md | 3 +-- README_JA.md | 3 +-- README_PTBR.md | 3 +-- README_RO.md | 3 +-- README_RS.md | 3 +-- README_RU.md | 3 +-- docker-compose.yaml | 1 - docs/README_CN.md | 1 - docs/docs/guides/local-development.md | 1 - 11 files changed, 7 insertions(+), 18 deletions(-) diff --git a/.env.example b/.env.example index 6f6e20891b..3905331771 100644 --- a/.env.example +++ b/.env.example @@ -85,7 +85,6 @@ TWITTER_TARGET_USERS= # Comma separated list of Twitter user names to TWITTER_RETRY_LIMIT= # Maximum retry attempts for Twitter login TWITTER_SPACES_ENABLE=false # Enable or disable Twitter Spaces logic -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= diff --git a/README_CN.md b/README_CN.md index 82115705cb..1ae519929b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -188,7 +188,6 @@ TWITTER_USERNAME= # Account username TWITTER_PASSWORD= # Account password TWITTER_EMAIL= # Account email -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -237,7 +236,7 @@ npx --no node-llama-cpp source download --gpu cuda ### 本地运行 添加 XAI_MODEL 并将其设置为上述 [使用 Llama 运行](#run-with-llama) 中的选项之一 -您可以将 X_SERVER_URL 和 XAI_API_KEY 留空,它会从 huggingface 下载模型并在本地查询 +您可以将 XAI_API_KEY 留空,它会从 huggingface 下载模型并在本地查询 # 客户端 diff --git a/README_ES.md b/README_ES.md index 7cafc7aca7..c2f3006ebc 100644 --- a/README_ES.md +++ b/README_ES.md @@ -99,7 +99,6 @@ TWITTER_USERNAME= # Nombre de usuario de la cuenta TWITTER_PASSWORD= # Contraseña de la cuenta TWITTER_EMAIL= # Correo electrónico de la cuenta -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -145,7 +144,7 @@ Asegúrese de tener instalado el CUDA Toolkit, incluyendo cuDNN y cuBLAS. ### Ejecución local -Agregue XAI_MODEL y configúrelo con una de las opciones de [Ejecutar con Llama](#ejecutar-con-llama) - puede dejar X_SERVER_URL y XAI_API_KEY en blanco, descargará el modelo de HuggingFace y realizará consultas localmente +Agregue XAI_MODEL y configúrelo con una de las opciones de [Ejecutar con Llama](#ejecutar-con-llama) - puede dejar XAI_API_KEY en blanco, descargará el modelo de HuggingFace y realizará consultas localmente # Clientes diff --git a/README_JA.md b/README_JA.md index fc1f084ca7..d9368db201 100644 --- a/README_JA.md +++ b/README_JA.md @@ -97,7 +97,6 @@ TWITTER_USERNAME= # アカウントのユーザー名 TWITTER_PASSWORD= # アカウントのパスワード TWITTER_EMAIL= # アカウントのメール -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -145,7 +144,7 @@ CUDA Toolkit、cuDNN、cuBLASをインストールしていることを確認し ### ローカルでの実行 -XAI_MODELを追加し、[Llamaでの実行](#run-with-llama)のオプションのいずれかに設定 - X_SERVER_URLとXAI_API_KEYを空白のままにしておくと、huggingfaceからモデルをダウンロードし、ローカルでクエリを実行します。 +XAI_MODELを追加し、[Llamaでの実行](#run-with-llama)のオプションのいずれかに設定 - XAI_API_KEYを空白のままにしておくと、huggingfaceからモデルをダウンロードし、ローカルでクエリを実行します。 # クライアント diff --git a/README_PTBR.md b/README_PTBR.md index db025a90d0..6b1d535911 100644 --- a/README_PTBR.md +++ b/README_PTBR.md @@ -99,7 +99,6 @@ TWITTER_USERNAME= # Nome de usuário da conta TWITTER_PASSWORD= # Senha da conta TWITTER_EMAIL= # Email da conta -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -147,7 +146,7 @@ Certifique-se de ter instalado o CUDA Toolkit, incluindo cuDNN e cuBLAS. ### Executando localmente -Adicione XAI_MODEL e configure-o para uma das opções acima de [Executar com Llama](#executar-com-llama) - você pode deixar X_SERVER_URL e XAI_API_KEY em branco, ele baixa o modelo do huggingface e faz consultas localmente +Adicione XAI_MODEL e configure-o para uma das opções acima de [Executar com Llama](#executar-com-llama) - você pode deixar XAI_API_KEY em branco, ele baixa o modelo do huggingface e faz consultas localmente # Clientes diff --git a/README_RO.md b/README_RO.md index 41780ef2dd..6334550afe 100644 --- a/README_RO.md +++ b/README_RO.md @@ -99,7 +99,6 @@ TWITTER_USERNAME= # Nome de usuário da conta TWITTER_PASSWORD= # Senha da conta TWITTER_EMAIL= # Email da conta -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -147,7 +146,7 @@ Asigurați-vă că ați instalat CUDA Toolkit, inclusiv cuDNN și cuBLAS. ### Rularea locală -Adăugați `XAI_MODEL` și setați-l la una dintre opțiunile de mai sus din [Rularea cu Llama](#rularea-cu-llama) – puteți lăsa `X_SERVER_URL` și `XAI_API_KEY` necompletate, modelul va fi descărcat de pe Hugging Face și interogările vor fi făcute local. +Adăugați `XAI_MODEL` și setați-l la una dintre opțiunile de mai sus din [Rularea cu Llama](#rularea-cu-llama) – puteți lăsa `XAI_API_KEY` necompletate, modelul va fi descărcat de pe Hugging Face și interogările vor fi făcute local. # Clienți diff --git a/README_RS.md b/README_RS.md index a58e5146fe..0c78324cf9 100644 --- a/README_RS.md +++ b/README_RS.md @@ -99,7 +99,6 @@ TWITTER_USERNAME= # Korisničko ime naloga TWITTER_PASSWORD= # Lozinka naloga TWITTER_EMAIL= # Email naloga -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -145,7 +144,7 @@ Uverite se da imate instaliran CUDA Toolkit, uključujući cuDNN i cuBLAS. ### Lokalno Pokretanje -Dodajte XAI_MODEL i konfigurišite ga sa jednom od opcija iz [Pokretanje sa Llama](#pokretanje-sa-llama) - možete ostaviti X_SERVER_URL i XAI_API_KEY praznim, preuzeće model sa HuggingFace i izvršiti upite lokalno +Dodajte XAI_MODEL i konfigurišite ga sa jednom od opcija iz [Pokretanje sa Llama](#pokretanje-sa-llama) - možete ostaviti XAI_API_KEY praznim, preuzeće model sa HuggingFace i izvršiti upite lokalno # Klijenti diff --git a/README_RU.md b/README_RU.md index 6a3ce2b0db..c12bd32cd6 100644 --- a/README_RU.md +++ b/README_RU.md @@ -115,7 +115,6 @@ TWITTER_USERNAME= # Имя пользователя аккаунта TWITTER_PASSWORD= # Пароль аккаунта TWITTER_EMAIL= # Email аккаунта -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= @@ -164,7 +163,7 @@ npx --no node-llama-cpp source download --gpu cuda ### Локальный запуск -Добавьте `XAI_MODEL` и установите его в одно из вышеуказанных значений из [Запуск с Llama](#run-with-llama). Вы можете оставить `X_SERVER_URL` и `XAI_API_KEY` пустыми — модель будет загружена с huggingface и обработана локально. +Добавьте `XAI_MODEL` и установите его в одно из вышеуказанных значений из [Запуск с Llama](#run-with-llama). Вы можете оставить `XAI_API_KEY` пустыми — модель будет загружена с huggingface и обработана локально. # Клиенты diff --git a/docker-compose.yaml b/docker-compose.yaml index 01acb4400e..507f363daf 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -25,7 +25,6 @@ services: - TWITTER_USERNAME= - TWITTER_PASSWORD= - TWITTER_EMAIL= - - X_SERVER_URL=https://api.red-pill.ai/v1 - BIRDEYE_API_KEY= - SOL_ADDRESS=So11111111111111111111111111111111111111112 - SLIPPAGE=1 diff --git a/docs/README_CN.md b/docs/README_CN.md index 4da03a5730..216c13001b 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -95,7 +95,6 @@ TWITTER_USERNAME= # Account username TWITTER_PASSWORD= # Account password TWITTER_EMAIL= # Account email -X_SERVER_URL= XAI_API_KEY= XAI_MODEL= diff --git a/docs/docs/guides/local-development.md b/docs/docs/guides/local-development.md index f0b24bc586..ce06bde9cc 100644 --- a/docs/docs/guides/local-development.md +++ b/docs/docs/guides/local-development.md @@ -75,7 +75,6 @@ Configure essential development variables: ```bash # Minimum required for local development OPENAI_API_KEY=sk-* # Optional, for OpenAI features -X_SERVER_URL= # Leave blank for local inference XAI_API_KEY= # Leave blank for local inference XAI_MODEL=meta-llama/Llama-3.1-7b-instruct # Local model ``` From d5e4a7adf58a47aff612b907aeab44a7884b5302 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Mon, 6 Jan 2025 19:24:11 -0500 Subject: [PATCH 42/58] Limit the number of timelines fetched --- packages/client-twitter/src/base.ts | 9 +++++---- packages/client-twitter/src/post.ts | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts index 34aba56957..d49091ccbf 100644 --- a/packages/client-twitter/src/base.ts +++ b/packages/client-twitter/src/base.ts @@ -318,7 +318,7 @@ export class ClientBase extends EventEmitter { return processedTimeline; } - async fetchTimelineForActions(): Promise { + async fetchTimelineForActions(count: number): Promise { elizaLogger.debug("fetching timeline for actions"); const agentUsername = this.twitterConfig.TWITTER_USERNAME; @@ -326,8 +326,8 @@ export class ClientBase extends EventEmitter { const homeTimeline = this.twitterConfig.ACTION_TIMELINE_TYPE === ActionTimelineType.Following - ? await this.twitterClient.fetchFollowingTimeline(20, []) - : await this.twitterClient.fetchHomeTimeline(20, []); + ? await this.twitterClient.fetchFollowingTimeline(count, []) + : await this.twitterClient.fetchHomeTimeline(count, []); return homeTimeline .map((tweet) => ({ @@ -357,7 +357,8 @@ export class ClientBase extends EventEmitter { (media) => media.type === "video" ) || [], })) - .filter((tweet) => tweet.username !== agentUsername); // do not perform action on self-tweets + .filter((tweet) => tweet.username !== agentUsername) // do not perform action on self-tweets + .slice(0, count); } async fetchSearchTweets( diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index 535b176518..7290885bb3 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -627,10 +627,7 @@ export class TwitterPostClient { "twitter" ); - // TODO: Once the 'count' parameter is fixed in the 'fetchTimeline' method of the 'agent-twitter-client', - // we should enable the ability to control the number of items fetched here. - // Related issue: https://github.com/elizaOS/agent-twitter-client/issues/43 - const homeTimeline = await this.client.fetchTimelineForActions(); + const homeTimeline = await this.client.fetchTimelineForActions(15); const maxActionsProcessing = this.client.twitterConfig.MAX_ACTIONS_PROCESSING; const processedTimelines = []; From 4d3ce33b67121e21a593f74843b8408479516bf3 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Mon, 6 Jan 2025 19:25:31 -0500 Subject: [PATCH 43/58] make a constant --- packages/client-twitter/src/post.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/client-twitter/src/post.ts b/packages/client-twitter/src/post.ts index 7290885bb3..9dc1694efd 100644 --- a/packages/client-twitter/src/post.ts +++ b/packages/client-twitter/src/post.ts @@ -19,6 +19,8 @@ import { DEFAULT_MAX_TWEET_LENGTH } from "./environment.ts"; import { State } from "@elizaos/core"; import { ActionResponse } from "@elizaos/core"; +const MAX_TIMELINES_TO_FETCH = 15; + const twitterPostTemplate = ` # Areas of Expertise {{knowledge}} @@ -627,7 +629,9 @@ export class TwitterPostClient { "twitter" ); - const homeTimeline = await this.client.fetchTimelineForActions(15); + const homeTimeline = await this.client.fetchTimelineForActions( + MAX_TIMELINES_TO_FETCH + ); const maxActionsProcessing = this.client.twitterConfig.MAX_ACTIONS_PROCESSING; const processedTimelines = []; From 872fd0f749f9ccd79c29cc777be5be343a590548 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Mon, 6 Jan 2025 19:27:12 -0500 Subject: [PATCH 44/58] add comment --- packages/client-twitter/src/base.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/client-twitter/src/base.ts b/packages/client-twitter/src/base.ts index d49091ccbf..5ad8b67039 100644 --- a/packages/client-twitter/src/base.ts +++ b/packages/client-twitter/src/base.ts @@ -359,6 +359,9 @@ export class ClientBase extends EventEmitter { })) .filter((tweet) => tweet.username !== agentUsername) // do not perform action on self-tweets .slice(0, count); + // TODO: Once the 'count' parameter is fixed in the 'fetchTimeline' method of the 'agent-twitter-client', + // this workaround can be removed. + // Related issue: https://github.com/elizaOS/agent-twitter-client/issues/43 } async fetchSearchTweets( From 3457f66a3469821dddcc06e4854a8f58f0804be2 Mon Sep 17 00:00:00 2001 From: Afanti <127061691+threewebcode@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:28:40 +0800 Subject: [PATCH 45/58] docs: it is processAction instead of triggerAction There is only processAction in runtime instance. Update them correctly. --- docs/docs/packages/plugins.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/docs/packages/plugins.md b/docs/docs/packages/plugins.md index bda974c03e..448d7e4e8d 100644 --- a/docs/docs/packages/plugins.md +++ b/docs/docs/packages/plugins.md @@ -263,7 +263,7 @@ runtime.character.settings.secrets = { **Example Call** ```typescript -const response = await runtime.triggerAction("SEND_MASS_PAYOUT", { +const response = await runtime.processAction("SEND_MASS_PAYOUT", { receivingAddresses: [ "0xA0ba2ACB5846A54834173fB0DD9444F756810f06", "0xF14F2c49aa90BaFA223EE074C1C33b59891826bF", @@ -388,7 +388,7 @@ All contract deployments and interactions are logged to a CSV file for record-ke 1. **ERC20 Token** ```typescript - const response = await runtime.triggerAction("DEPLOY_TOKEN_CONTRACT", { + const response = await runtime.processAction("DEPLOY_TOKEN_CONTRACT", { contractType: "ERC20", name: "MyToken", symbol: "MTK", @@ -400,7 +400,7 @@ All contract deployments and interactions are logged to a CSV file for record-ke 2. **NFT Collection** ```typescript - const response = await runtime.triggerAction("DEPLOY_TOKEN_CONTRACT", { + const response = await runtime.processAction("DEPLOY_TOKEN_CONTRACT", { contractType: "ERC721", name: "MyNFT", symbol: "MNFT", @@ -411,7 +411,7 @@ All contract deployments and interactions are logged to a CSV file for record-ke 3. **Multi-token Collection** ```typescript - const response = await runtime.triggerAction("DEPLOY_TOKEN_CONTRACT", { + const response = await runtime.processAction("DEPLOY_TOKEN_CONTRACT", { contractType: "ERC1155", name: "MyMultiToken", symbol: "MMT", @@ -423,7 +423,7 @@ All contract deployments and interactions are logged to a CSV file for record-ke **Contract Interaction Example:** ```typescript -const response = await runtime.triggerAction("INVOKE_CONTRACT", { +const response = await runtime.processAction("INVOKE_CONTRACT", { contractAddress: "0x123...", method: "transfer", abi: [...], @@ -597,7 +597,7 @@ The Webhook Plugin enables Eliza to interact with the Coinbase SDK to create and To create a webhook: ```typescript -const response = await runtime.triggerAction("CREATE_WEBHOOK", { +const response = await runtime.processAction("CREATE_WEBHOOK", { networkId: "base", eventType: "transfers", notificationUri: "https://your-notification-uri.com", From 8e5fc5393bd35b430be4afe48f0596ab4c2746d1 Mon Sep 17 00:00:00 2001 From: genesis0000 Date: Tue, 7 Jan 2025 10:18:38 +0700 Subject: [PATCH 46/58] feat: support for eternalai provider can make request with chain_id extra data in body --- .env.example | 3 ++- packages/core/src/generation.ts | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 3905331771..af7f8611ba 100644 --- a/.env.example +++ b/.env.example @@ -19,8 +19,9 @@ IMAGE_OPENAI_MODEL= # Default: dall-e-3 # Eternal AI's Decentralized Inference API ETERNALAI_URL= ETERNALAI_MODEL= # Default: "neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16" +ETERNALAI_CHAIN_ID=45762 #Default: "45762" ETERNALAI_API_KEY= -ETERNAL_AI_LOG_REQUEST=false #Default: false +ETERNAL_AI_LOG_REQUEST=false #Default: false GROK_API_KEY= # GROK/xAI API Key GROQ_API_KEY= # Starts with gsk_ diff --git a/packages/core/src/generation.ts b/packages/core/src/generation.ts index 8d2397aa7f..a00a89bbc7 100644 --- a/packages/core/src/generation.ts +++ b/packages/core/src/generation.ts @@ -389,6 +389,12 @@ export async function generateText({ apiKey, baseURL: endpoint, fetch: async (url: string, options: any) => { + const chain_id = runtime.getSetting("ETERNALAI_CHAIN_ID") || "45762" + if (options?.body) { + const body = JSON.parse(options.body); + body.chain_id = chain_id; + options.body = JSON.stringify(body); + } const fetching = await runtime.fetch(url, options); if ( parseBooleanFromText( @@ -400,12 +406,16 @@ export async function generateText({ JSON.stringify(options, null, 2) ); const clonedResponse = fetching.clone(); - clonedResponse.json().then((data) => { - elizaLogger.info( - "Response data: ", - JSON.stringify(data, null, 2) - ); - }); + try { + clonedResponse.json().then((data) => { + elizaLogger.info( + "Response data: ", + JSON.stringify(data, null, 2) + ); + }); + } catch (e) { + elizaLogger.debug(e); + } } return fetching; }, From 45598fc2d0ee938bec1baaac2e597d831377f032 Mon Sep 17 00:00:00 2001 From: bendanzhentan <455462586@qq.com> Date: Tue, 7 Jan 2025 12:20:05 +0800 Subject: [PATCH 47/58] refactor: remove unused imports --- packages/plugin-conflux/src/actions/confiPump.ts | 2 +- packages/plugin-conflux/src/types.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/plugin-conflux/src/actions/confiPump.ts b/packages/plugin-conflux/src/actions/confiPump.ts index ada3c50f8c..7ebae97885 100644 --- a/packages/plugin-conflux/src/actions/confiPump.ts +++ b/packages/plugin-conflux/src/actions/confiPump.ts @@ -17,7 +17,7 @@ import { Account, } from "viem"; import { privateKeyToAccount } from "viem/accounts"; -import { confluxESpaceTestnet, confluxESpace } from "viem/chains"; +import { confluxESpaceTestnet } from "viem/chains"; import { parseUnits, getAddress } from "viem/utils"; import { confluxTransferTemplate } from "../templates/transfer"; import { diff --git a/packages/plugin-conflux/src/types.ts b/packages/plugin-conflux/src/types.ts index 9764386437..ac6615bded 100644 --- a/packages/plugin-conflux/src/types.ts +++ b/packages/plugin-conflux/src/types.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { Content } from "@elizaos/core"; export const TransferSchema = z.object({ to: z.string(), From ac92b62ba85042071d4637dc262e0811ef406d16 Mon Sep 17 00:00:00 2001 From: Shlok Khemani Date: Tue, 7 Jan 2025 10:53:02 +0530 Subject: [PATCH 48/58] chore: add embedding tests --- packages/core/src/tests/embedding.test.ts | 201 ++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 packages/core/src/tests/embedding.test.ts diff --git a/packages/core/src/tests/embedding.test.ts b/packages/core/src/tests/embedding.test.ts new file mode 100644 index 0000000000..3d83135dd2 --- /dev/null +++ b/packages/core/src/tests/embedding.test.ts @@ -0,0 +1,201 @@ +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { + embed, + getEmbeddingConfig, + getEmbeddingType, + getEmbeddingZeroVector, +} from "../embedding.ts"; +import { IAgentRuntime, ModelProviderName } from "../types.ts"; +import settings from "../settings.ts"; + +// Mock environment-related settings +vi.mock("../settings", () => ({ + default: { + USE_OPENAI_EMBEDDING: "false", + USE_OLLAMA_EMBEDDING: "false", + USE_GAIANET_EMBEDDING: "false", + OPENAI_API_KEY: "mock-openai-key", + OPENAI_API_URL: "https://api.openai.com/v1", + GAIANET_API_KEY: "mock-gaianet-key", + OLLAMA_EMBEDDING_MODEL: "mxbai-embed-large", + GAIANET_EMBEDDING_MODEL: "nomic-embed", + }, +})); + +// Mock fastembed module for local embeddings +vi.mock("fastembed", () => ({ + FlagEmbedding: { + init: vi.fn().mockResolvedValue({ + queryEmbed: vi + .fn() + .mockResolvedValue(new Float32Array(384).fill(0.1)), + }), + }, + EmbeddingModel: { + BGESmallENV15: "BGE-small-en-v1.5", + }, +})); + +// Mock global fetch for remote embedding requests +const mockFetch = vi.fn(); +(global as any).fetch = mockFetch; + +describe("Embedding Module", () => { + let mockRuntime: IAgentRuntime; + + beforeEach(() => { + // Prepare a mock runtime + mockRuntime = { + character: { + modelProvider: ModelProviderName.OLLAMA, + modelEndpointOverride: null, + }, + token: "mock-token", + messageManager: { + getCachedEmbeddings: vi.fn().mockResolvedValue([]), + }, + } as unknown as IAgentRuntime; + + vi.clearAllMocks(); + mockFetch.mockReset(); + }); + + describe("getEmbeddingConfig", () => { + test("should return BGE config by default", () => { + const config = getEmbeddingConfig(); + expect(config.dimensions).toBe(384); + expect(config.model).toBe("BGE-small-en-v1.5"); + expect(config.provider).toBe("BGE"); + }); + + test("should return OpenAI config when USE_OPENAI_EMBEDDING is true", () => { + vi.mocked(settings).USE_OPENAI_EMBEDDING = "true"; + const config = getEmbeddingConfig(); + expect(config.dimensions).toBe(1536); + expect(config.model).toBe("text-embedding-3-small"); + expect(config.provider).toBe("OpenAI"); + }); + }); + + describe("getEmbeddingType", () => { + test("should return 'remote' for Ollama provider", () => { + const type = getEmbeddingType(mockRuntime); + expect(type).toBe("remote"); + }); + + test("should return 'remote' for OpenAI provider", () => { + mockRuntime.character.modelProvider = ModelProviderName.OPENAI; + const type = getEmbeddingType(mockRuntime); + expect(type).toBe("remote"); + }); + }); + + describe("getEmbeddingZeroVector", () => { + beforeEach(() => { + vi.mocked(settings).USE_OPENAI_EMBEDDING = "false"; + vi.mocked(settings).USE_OLLAMA_EMBEDDING = "false"; + vi.mocked(settings).USE_GAIANET_EMBEDDING = "false"; + }); + + test("should return 384-length zero vector by default (BGE)", () => { + const vector = getEmbeddingZeroVector(); + expect(vector).toHaveLength(384); + expect(vector.every((val) => val === 0)).toBe(true); + }); + + test("should return 1536-length zero vector for OpenAI if enabled", () => { + vi.mocked(settings).USE_OPENAI_EMBEDDING = "true"; + const vector = getEmbeddingZeroVector(); + expect(vector).toHaveLength(1536); + expect(vector.every((val) => val === 0)).toBe(true); + }); + }); + + describe("embed function", () => { + beforeEach(() => { + // Mock a successful remote response with an example 384-dim embedding + mockFetch.mockResolvedValue({ + ok: true, + json: () => + Promise.resolve({ + data: [{ embedding: new Array(384).fill(0.1) }], + }), + }); + }); + + test("should return an empty array for empty input text", async () => { + const result = await embed(mockRuntime, ""); + expect(result).toEqual([]); + }); + + test("should return cached embedding if it already exists", async () => { + const cachedEmbedding = new Array(384).fill(0.5); + mockRuntime.messageManager.getCachedEmbeddings = vi + .fn() + .mockResolvedValue([{ embedding: cachedEmbedding }]); + + const result = await embed(mockRuntime, "test input"); + expect(result).toBe(cachedEmbedding); + }); + + test("should handle local embedding successfully (fastembed fallback)", async () => { + // By default, it tries local first if in Node. + // Then uses the mock fastembed response above. + const result = await embed(mockRuntime, "test input"); + expect(result).toHaveLength(384); + expect(result.every((v) => typeof v === "number")).toBe(true); + }); + + test("should fallback to remote if local embedding fails", async () => { + // Force fastembed import to fail + vi.mock("fastembed", () => { + throw new Error("Module not found"); + }); + + // Mock a valid remote response + const mockResponse = { + ok: true, + json: () => + Promise.resolve({ + data: [{ embedding: new Array(384).fill(0.1) }], + }), + }; + mockFetch.mockResolvedValueOnce(mockResponse); + + const result = await embed(mockRuntime, "test input"); + expect(result).toHaveLength(384); + expect(mockFetch).toHaveBeenCalled(); + }); + + test("should throw on remote embedding if fetch fails", async () => { + mockFetch.mockRejectedValueOnce(new Error("API Error")); + vi.mocked(settings).USE_OPENAI_EMBEDDING = "true"; // Force remote + + await expect(embed(mockRuntime, "test input")).rejects.toThrow( + "API Error" + ); + }); + + test("should throw on non-200 remote response", async () => { + const errorResponse = { + ok: false, + status: 400, + statusText: "Bad Request", + text: () => Promise.resolve("Invalid input"), + }; + mockFetch.mockResolvedValueOnce(errorResponse); + vi.mocked(settings).USE_OPENAI_EMBEDDING = "true"; // Force remote + + await expect(embed(mockRuntime, "test input")).rejects.toThrow( + "Embedding API Error" + ); + }); + + test("should handle concurrent embedding requests", async () => { + const promises = Array(5) + .fill(null) + .map(() => embed(mockRuntime, "concurrent test")); + await expect(Promise.all(promises)).resolves.toBeDefined(); + }); + }); +}); From e342e4bedcd958b117762711efbb966fae4f22c4 Mon Sep 17 00:00:00 2001 From: 0xRider Date: Mon, 6 Jan 2025 21:58:49 -0800 Subject: [PATCH 49/58] chore: more specific rpc urls --- .env.example | 4 ++-- README_CN.md | 2 +- README_ES.md | 2 +- README_JA.md | 2 +- README_PTBR.md | 2 +- README_RO.md | 2 +- README_RS.md | 2 +- README_RU.md | 2 +- docker-compose.yaml | 2 +- docs/README.md | 2 +- docs/README_CN.md | 2 +- docs/README_DE.md | 2 +- docs/README_ES.md | 2 +- docs/README_FR.md | 2 +- docs/README_TH.md | 2 +- docs/docs/advanced/eliza-in-tee.md | 2 +- docs/docs/api/_media/README_CN.md | 2 +- docs/docs/api/_media/README_FR.md | 2 +- docs/docs/api/_media/README_JA.md | 2 +- docs/docs/api/_media/README_KOR.md | 2 +- docs/docs/api/index.md | 2 +- packages/core/.env.test | 2 +- packages/plugin-near/README.md | 2 +- packages/plugin-near/src/actions/swap.ts | 4 ++-- packages/plugin-near/src/actions/transfer.ts | 2 +- packages/plugin-near/src/environment.ts | 4 ++-- packages/plugin-solana-agentkit/src/actions/createToken.ts | 2 +- packages/plugin-solana/README.MD | 2 +- packages/plugin-solana/src/actions/swapDao.ts | 2 +- packages/plugin-solana/src/actions/swapUtils.ts | 2 +- packages/plugin-solana/src/actions/transfer.ts | 2 +- packages/plugin-solana/src/environment.ts | 6 ++++-- packages/plugin-solana/src/evaluators/trust.ts | 2 +- .../plugin-solana/src/providers/simulationSellingService.ts | 2 +- packages/plugin-solana/src/providers/trustScoreProvider.ts | 2 +- packages/plugin-solana/src/providers/wallet.ts | 2 +- 36 files changed, 42 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index 3905331771..7281d1b009 100644 --- a/.env.example +++ b/.env.example @@ -214,7 +214,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 BASE_MINT=So11111111111111111111111111111111111111112 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= # Telegram Configuration @@ -345,7 +345,7 @@ NEAR_WALLET_SECRET_KEY= NEAR_WALLET_PUBLIC_KEY= NEAR_ADDRESS= SLIPPAGE=1 -RPC_URL=https://rpc.testnet.near.org +NEAR_RPC_URL=https://rpc.testnet.near.org NEAR_NETWORK=testnet # or mainnet # ZKsync Era Configuration diff --git a/README_CN.md b/README_CN.md index 1ae519929b..a59e97eb4e 100644 --- a/README_CN.md +++ b/README_CN.md @@ -210,7 +210,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/README_ES.md b/README_ES.md index c2f3006ebc..55530380be 100644 --- a/README_ES.md +++ b/README_ES.md @@ -120,7 +120,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= ## Telegram diff --git a/README_JA.md b/README_JA.md index d9368db201..30d759de8d 100644 --- a/README_JA.md +++ b/README_JA.md @@ -119,7 +119,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/README_PTBR.md b/README_PTBR.md index 6b1d535911..c621d26ae3 100644 --- a/README_PTBR.md +++ b/README_PTBR.md @@ -121,7 +121,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/README_RO.md b/README_RO.md index 6334550afe..c65b85aafc 100644 --- a/README_RO.md +++ b/README_RO.md @@ -121,7 +121,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/README_RS.md b/README_RS.md index 0c78324cf9..d30826839c 100644 --- a/README_RS.md +++ b/README_RS.md @@ -120,7 +120,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= ## Telegram diff --git a/README_RU.md b/README_RU.md index c12bd32cd6..3b6015886f 100644 --- a/README_RU.md +++ b/README_RU.md @@ -137,7 +137,7 @@ BIRDEYE_API_KEY= # API-ключ для BirdEye SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= # API-ключ Helius diff --git a/docker-compose.yaml b/docker-compose.yaml index 507f363daf..6e8432d0c6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -28,7 +28,7 @@ services: - BIRDEYE_API_KEY= - SOL_ADDRESS=So11111111111111111111111111111111111111112 - SLIPPAGE=1 - - RPC_URL=https://api.mainnet-beta.solana.com + - SOLANA_RPC_URL=https://api.mainnet-beta.solana.com - HELIUS_API_KEY= - SERVER_PORT=3000 - WALLET_SECRET_SALT=secret_salt diff --git a/docs/README.md b/docs/README.md index 3b1791b826..0a4e37c477 100644 --- a/docs/README.md +++ b/docs/README.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/README_CN.md b/docs/README_CN.md index 216c13001b..9912c37c34 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -117,7 +117,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/README_DE.md b/docs/README_DE.md index 632e0cd606..0f4005ef9b 100644 --- a/docs/README_DE.md +++ b/docs/README_DE.md @@ -114,7 +114,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= ## Telegram diff --git a/docs/README_ES.md b/docs/README_ES.md index 419ca205ee..d578f1fe06 100644 --- a/docs/README_ES.md +++ b/docs/README_ES.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/README_FR.md b/docs/README_FR.md index 27f102764f..23c843db84 100644 --- a/docs/README_FR.md +++ b/docs/README_FR.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/README_TH.md b/docs/README_TH.md index f2534b1fc7..ad82443eb8 100644 --- a/docs/README_TH.md +++ b/docs/README_TH.md @@ -114,7 +114,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/docs/advanced/eliza-in-tee.md b/docs/docs/advanced/eliza-in-tee.md index d906025459..76d2e9e285 100644 --- a/docs/docs/advanced/eliza-in-tee.md +++ b/docs/docs/advanced/eliza-in-tee.md @@ -272,7 +272,7 @@ services: - BIRDEYE_API_KEY=$BIRDEYE_API_KEY - SOL_ADDRESS=So11111111111111111111111111111111111111112 - SLIPPAGE=1 - - RPC_URL=https://api.mainnet-beta.solana.com + - SOLANA_RPC_URL=https://api.mainnet-beta.solana.com - HELIUS_API_KEY=$HELIUS_API_KEY - SERVER_PORT=3000 - WALLET_SECRET_SALT=$WALLET_SECRET_SALT diff --git a/docs/docs/api/_media/README_CN.md b/docs/docs/api/_media/README_CN.md index ae4f7a159a..39f2a2be3b 100644 --- a/docs/docs/api/_media/README_CN.md +++ b/docs/docs/api/_media/README_CN.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/docs/api/_media/README_FR.md b/docs/docs/api/_media/README_FR.md index 41b92f055f..57f8b970f5 100644 --- a/docs/docs/api/_media/README_FR.md +++ b/docs/docs/api/_media/README_FR.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/docs/api/_media/README_JA.md b/docs/docs/api/_media/README_JA.md index 3bd9302ccb..a85ef88ecf 100644 --- a/docs/docs/api/_media/README_JA.md +++ b/docs/docs/api/_media/README_JA.md @@ -120,7 +120,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/docs/api/_media/README_KOR.md b/docs/docs/api/_media/README_KOR.md index 555d037d9f..71ef879745 100644 --- a/docs/docs/api/_media/README_KOR.md +++ b/docs/docs/api/_media/README_KOR.md @@ -118,7 +118,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= diff --git a/docs/docs/api/index.md b/docs/docs/api/index.md index 4f3c232dcd..ed33e1393f 100644 --- a/docs/docs/api/index.md +++ b/docs/docs/api/index.md @@ -123,7 +123,7 @@ BIRDEYE_API_KEY= SOL_ADDRESS=So11111111111111111111111111111111111111112 SLIPPAGE=1 -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com HELIUS_API_KEY= ## Telegram diff --git a/packages/core/.env.test b/packages/core/.env.test index d295bdfb3b..ef68e98847 100644 --- a/packages/core/.env.test +++ b/packages/core/.env.test @@ -2,5 +2,5 @@ TEST_DATABASE_CLIENT=sqlite NODE_ENV=test MAIN_WALLET_ADDRESS=TEST_MAIN_WALLET_ADDRESS_VALUE OPENAI_API_KEY=TEST_OPENAI_API_KEY_VALUE -RPC_URL=https://api.mainnet-beta.solana.com +SOLANA_RPC_URL=https://api.mainnet-beta.solana.com WALLET_PUBLIC_KEY=2weMjPLLybRMMva1fM3U31goWWrCpF59CHWNhnCJ9Vyh \ No newline at end of file diff --git a/packages/plugin-near/README.md b/packages/plugin-near/README.md index b535dd2eeb..1af36b1f6f 100644 --- a/packages/plugin-near/README.md +++ b/packages/plugin-near/README.md @@ -34,7 +34,7 @@ NEAR_WALLET_SECRET_KEY=your-wallet-private-key NEAR_WALLET_PUBLIC_KEY=your-wallet-public-key NEAR_ADDRESS=your-account.near NEAR_NETWORK=testnet # mainnet or testnet -RPC_URL=https://rpc.testnet.near.org +NEAR_RPC_URL=https://rpc.testnet.near.org SLIPPAGE=0.01 # 1% slippage tolerance ``` diff --git a/packages/plugin-near/src/actions/swap.ts b/packages/plugin-near/src/actions/swap.ts index 5d1ebbe3db..bf2b466fdc 100644 --- a/packages/plugin-near/src/actions/swap.ts +++ b/packages/plugin-near/src/actions/swap.ts @@ -55,7 +55,7 @@ async function swapToken( const tokenOut = await ftGetTokenMetadata(outputTokenId); const networkId = runtime.getSetting("NEAR_NETWORK") || "testnet"; const nodeUrl = - runtime.getSetting("RPC_URL") || "https://rpc.testnet.near.org"; + runtime.getSetting("NEAR_RPC_URL") || "https://rpc.testnet.near.org"; // Get all pools for estimation // ratedPools, unRatedPools, @@ -257,7 +257,7 @@ export const executeSwap: Action = { networkId: runtime.getSetting("NEAR_NETWORK") || "testnet", keyStore, nodeUrl: - runtime.getSetting("RPC_URL") || + runtime.getSetting("NEAR_RPC_URL") || "https://rpc.testnet.near.org", }); diff --git a/packages/plugin-near/src/actions/transfer.ts b/packages/plugin-near/src/actions/transfer.ts index ab37f5030e..02444358e4 100644 --- a/packages/plugin-near/src/actions/transfer.ts +++ b/packages/plugin-near/src/actions/transfer.ts @@ -64,7 +64,7 @@ async function transferNEAR( ): Promise { const networkId = runtime.getSetting("NEAR_NETWORK") || "testnet"; const nodeUrl = - runtime.getSetting("RPC_URL") || "https://rpc.testnet.near.org"; + runtime.getSetting("NEAR_RPC_URL") || "https://rpc.testnet.near.org"; const accountId = runtime.getSetting("NEAR_ADDRESS"); const secretKey = runtime.getSetting("NEAR_WALLET_SECRET_KEY"); diff --git a/packages/plugin-near/src/environment.ts b/packages/plugin-near/src/environment.ts index eef8c582fa..c00387dfb1 100644 --- a/packages/plugin-near/src/environment.ts +++ b/packages/plugin-near/src/environment.ts @@ -9,7 +9,7 @@ export const nearEnvSchema = z.object({ NEAR_WALLET_PUBLIC_KEY: z.string().min(1, "Wallet public key is required"), NEAR_ADDRESS: z.string().min(1, "Near address is required"), SLIPPAGE: z.string().min(1, "Slippage is required"), - RPC_URL: z.string().min(1, "RPC URL is required"), + NEAR_RPC_URL: z.string().min(1, "RPC URL is required"), networkId: z.string(), nodeUrl: z.string(), walletUrl: z.string(), @@ -87,7 +87,7 @@ export async function validateNearConfig( NEAR_ADDRESS: runtime.getSetting("NEAR_ADDRESS") || process.env.NEAR_ADDRESS, SLIPPAGE: runtime.getSetting("SLIPPAGE") || process.env.SLIPPAGE, - RPC_URL: runtime.getSetting("RPC_URL") || process.env.RPC_URL, + NEAR_RPC_URL: runtime.getSetting("NEAR_RPC_URL") || process.env.NEAR_RPC_URL, ...envConfig, // Spread the environment-specific config }; diff --git a/packages/plugin-solana-agentkit/src/actions/createToken.ts b/packages/plugin-solana-agentkit/src/actions/createToken.ts index b0720348d5..46377f546a 100644 --- a/packages/plugin-solana-agentkit/src/actions/createToken.ts +++ b/packages/plugin-solana-agentkit/src/actions/createToken.ts @@ -104,7 +104,7 @@ export default { elizaLogger.log("Init solana agent kit..."); const solanaPrivatekey = runtime.getSetting("SOLANA_PRIVATE_KEY"); - const rpc = runtime.getSetting("RPC_URL"); + const rpc = runtime.getSetting("SOLANA_RPC_URL"); const openAIKey = runtime.getSetting("OPENAI_API_KEY"); const solanaAgentKit = new SolanaAgentKit( solanaPrivatekey, diff --git a/packages/plugin-solana/README.MD b/packages/plugin-solana/README.MD index 5f7f0d8fcb..7dd32ec479 100644 --- a/packages/plugin-solana/README.MD +++ b/packages/plugin-solana/README.MD @@ -69,7 +69,7 @@ const solanaEnvSchema = { WALLET_PUBLIC_KEY: string, SOL_ADDRESS: string, SLIPPAGE: string, - RPC_URL: string, + SOLANA_RPC_URL: string, HELIUS_API_KEY: string, BIRDEYE_API_KEY: string, }; diff --git a/packages/plugin-solana/src/actions/swapDao.ts b/packages/plugin-solana/src/actions/swapDao.ts index 732d8124f3..7a89d3f827 100644 --- a/packages/plugin-solana/src/actions/swapDao.ts +++ b/packages/plugin-solana/src/actions/swapDao.ts @@ -65,7 +65,7 @@ export const executeSwapForDAO: Action = { try { const connection = new Connection( - runtime.getSetting("RPC_URL") as string + runtime.getSetting("SOLANA_RPC_URL") as string ); const { keypair: authority } = await getWalletKey(runtime, true); diff --git a/packages/plugin-solana/src/actions/swapUtils.ts b/packages/plugin-solana/src/actions/swapUtils.ts index aabcd88a84..b23cc939f4 100644 --- a/packages/plugin-solana/src/actions/swapUtils.ts +++ b/packages/plugin-solana/src/actions/swapUtils.ts @@ -14,7 +14,7 @@ import { settings } from "@elizaos/core"; const solAddress = settings.SOL_ADDRESS; const SLIPPAGE = settings.SLIPPAGE; const connection = new Connection( - settings.RPC_URL || "https://api.mainnet-beta.solana.com" + settings.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com" ); const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/packages/plugin-solana/src/actions/transfer.ts b/packages/plugin-solana/src/actions/transfer.ts index 118e2b2468..0769e6b9f7 100644 --- a/packages/plugin-solana/src/actions/transfer.ts +++ b/packages/plugin-solana/src/actions/transfer.ts @@ -142,7 +142,7 @@ export default { true ); - const connection = new Connection(settings.RPC_URL!); + const connection = new Connection(settings.SOLANA_RPC_URL!); const mintPubkey = new PublicKey(content.tokenAddress); const recipientPubkey = new PublicKey(content.recipient); diff --git a/packages/plugin-solana/src/environment.ts b/packages/plugin-solana/src/environment.ts index e6931091c8..de7aa6e6e0 100644 --- a/packages/plugin-solana/src/environment.ts +++ b/packages/plugin-solana/src/environment.ts @@ -26,7 +26,7 @@ export const solanaEnvSchema = z z.object({ SOL_ADDRESS: z.string().min(1, "SOL address is required"), SLIPPAGE: z.string().min(1, "Slippage is required"), - RPC_URL: z.string().min(1, "RPC URL is required"), + SOLANA_RPC_URL: z.string().min(1, "RPC URL is required"), HELIUS_API_KEY: z.string().min(1, "Helius API key is required"), BIRDEYE_API_KEY: z.string().min(1, "Birdeye API key is required"), }) @@ -52,7 +52,9 @@ export async function validateSolanaConfig( SOL_ADDRESS: runtime.getSetting("SOL_ADDRESS") || process.env.SOL_ADDRESS, SLIPPAGE: runtime.getSetting("SLIPPAGE") || process.env.SLIPPAGE, - RPC_URL: runtime.getSetting("RPC_URL") || process.env.RPC_URL, + SOLANA_RPC_URL: + runtime.getSetting("SOLANA_RPC_URL") || + process.env.SOLANA_RPC_URL, HELIUS_API_KEY: runtime.getSetting("HELIUS_API_KEY") || process.env.HELIUS_API_KEY, diff --git a/packages/plugin-solana/src/evaluators/trust.ts b/packages/plugin-solana/src/evaluators/trust.ts index cc295638f4..d1b65a8398 100644 --- a/packages/plugin-solana/src/evaluators/trust.ts +++ b/packages/plugin-solana/src/evaluators/trust.ts @@ -158,7 +158,7 @@ async function handler(runtime: IAgentRuntime, message: Memory) { // create the wallet provider and token provider const walletProvider = new WalletProvider( new Connection( - runtime.getSetting("RPC_URL") || + runtime.getSetting("SOLANA_RPC_URL") || "https://api.mainnet-beta.solana.com" ), publicKey diff --git a/packages/plugin-solana/src/providers/simulationSellingService.ts b/packages/plugin-solana/src/providers/simulationSellingService.ts index a8398254fd..3a189a2fdf 100644 --- a/packages/plugin-solana/src/providers/simulationSellingService.ts +++ b/packages/plugin-solana/src/providers/simulationSellingService.ts @@ -39,7 +39,7 @@ export class SimulationSellingService { constructor(runtime: IAgentRuntime, trustScoreDb: TrustScoreDatabase) { this.trustScoreDb = trustScoreDb; - this.connection = new Connection(runtime.getSetting("RPC_URL")); + this.connection = new Connection(runtime.getSetting("SOLANA_RPC_URL")); this.baseMint = new PublicKey( runtime.getSetting("BASE_MINT") || "So11111111111111111111111111111111111111112" diff --git a/packages/plugin-solana/src/providers/trustScoreProvider.ts b/packages/plugin-solana/src/providers/trustScoreProvider.ts index 3034d64139..b9e046b0f9 100644 --- a/packages/plugin-solana/src/providers/trustScoreProvider.ts +++ b/packages/plugin-solana/src/providers/trustScoreProvider.ts @@ -67,7 +67,7 @@ export class TrustScoreManager { ) { this.tokenProvider = tokenProvider; this.trustScoreDb = trustScoreDb; - this.connection = new Connection(runtime.getSetting("RPC_URL")); + this.connection = new Connection(runtime.getSetting("SOLANA_RPC_URL")); this.baseMint = new PublicKey( runtime.getSetting("BASE_MINT") || "So11111111111111111111111111111111111111112" diff --git a/packages/plugin-solana/src/providers/wallet.ts b/packages/plugin-solana/src/providers/wallet.ts index 7e3c55580b..660631c8c8 100644 --- a/packages/plugin-solana/src/providers/wallet.ts +++ b/packages/plugin-solana/src/providers/wallet.ts @@ -374,7 +374,7 @@ const walletProvider: Provider = { const { publicKey } = await getWalletKey(runtime, false); const connection = new Connection( - runtime.getSetting("RPC_URL") || PROVIDER_CONFIG.DEFAULT_RPC + runtime.getSetting("SOLANA_RPC_URL") || PROVIDER_CONFIG.DEFAULT_RPC ); const provider = new WalletProvider(connection, publicKey); From 4accf39d1fd733ffc2e9e0d869be3ec16eeceb64 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:33:43 +0800 Subject: [PATCH 50/58] feat: improve twitter paragraph splitting if containing url --- packages/client-twitter/src/utils.ts | 65 +++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/packages/client-twitter/src/utils.ts b/packages/client-twitter/src/utils.ts index 32ef6247dc..4ea86e580c 100644 --- a/packages/client-twitter/src/utils.ts +++ b/packages/client-twitter/src/utils.ts @@ -310,11 +310,31 @@ function splitTweetContent(content: string, maxLength: number): string[] { return tweets; } -function splitParagraph(paragraph: string, maxLength: number): string[] { - // eslint-disable-next-line - const sentences = paragraph.match(/[^\.!\?]+[\.!\?]+|[^\.!\?]+$/g) || [ - paragraph, - ]; +function extractUrls(paragraph: string): { + textWithPlaceholders: string; + placeholderMap: Map; +} { + // replace https urls with placeholder + const urlRegex = /https?:\/\/[^\s]+/g; + const placeholderMap = new Map(); + + let urlIndex = 0; + const textWithPlaceholders = paragraph.replace(urlRegex, (match) => { + // twitter url would be considered as 23 characters + // <> is also 23 characters + const placeholder = `<>`; // Placeholder without . ? ! etc + placeholderMap.set(placeholder, match); + urlIndex++; + return placeholder; + }); + + return { textWithPlaceholders, placeholderMap }; +} + +function splitSentencesAndWords(text: string, maxLength: number): string[] { + // Split by periods, question marks and exclamation marks + // Note that URLs in text have been replaced with `<>` and won't be split by dots + const sentences = text.match(/[^\.!\?]+[\.!\?]+|[^\.!\?]+$/g) || [text]; const chunks: string[] = []; let currentChunk = ""; @@ -326,13 +346,16 @@ function splitParagraph(paragraph: string, maxLength: number): string[] { currentChunk = sentence; } } else { + // Can't fit more, push currentChunk to results if (currentChunk) { chunks.push(currentChunk.trim()); } + + // If current sentence itself is less than or equal to maxLength if (sentence.length <= maxLength) { currentChunk = sentence; } else { - // Split long sentence into smaller pieces + // Need to split sentence by spaces const words = sentence.split(" "); currentChunk = ""; for (const word of words) { @@ -355,9 +378,39 @@ function splitParagraph(paragraph: string, maxLength: number): string[] { } } + // Handle remaining content if (currentChunk) { chunks.push(currentChunk.trim()); } return chunks; } + +function restoreUrls( + chunks: string[], + placeholderMap: Map +): string[] { + return chunks.map((chunk) => { + // Replace all <> in chunk back to original URLs using regex + return chunk.replace(/<>/g, (match) => { + const original = placeholderMap.get(match); + return original || match; // Return placeholder if not found (theoretically won't happen) + }); + }); +} + +function splitParagraph(paragraph: string, maxLength: number): string[] { + // 1) Extract URLs and replace with placeholders + const { textWithPlaceholders, placeholderMap } = extractUrls(paragraph); + + // 2) Use first section's logic to split by sentences first, then do secondary split + const splittedChunks = splitSentencesAndWords( + textWithPlaceholders, + maxLength + ); + + // 3) Replace placeholders back to original URLs + const restoredChunks = restoreUrls(splittedChunks, placeholderMap); + + return restoredChunks; +} \ No newline at end of file From fb2954dd26d66a40b9ebec38632ae8164e7a2f11 Mon Sep 17 00:00:00 2001 From: genesis0000 Date: Tue, 7 Jan 2025 14:13:56 +0700 Subject: [PATCH 51/58] feat: support for eternalai provider can write request/response, change name in env --- .env.example | 2 +- packages/core/generation.ts | 2 +- packages/core/src/generation.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index af7f8611ba..157b364aa4 100644 --- a/.env.example +++ b/.env.example @@ -21,7 +21,7 @@ ETERNALAI_URL= ETERNALAI_MODEL= # Default: "neuralmagic/Meta-Llama-3.1-405B-Instruct-quantized.w4a16" ETERNALAI_CHAIN_ID=45762 #Default: "45762" ETERNALAI_API_KEY= -ETERNAL_AI_LOG_REQUEST=false #Default: false +ETERNALAI_LOG=false #Default: false GROK_API_KEY= # GROK/xAI API Key GROQ_API_KEY= # Starts with gsk_ diff --git a/packages/core/generation.ts b/packages/core/generation.ts index 583e678793..74d4123773 100644 --- a/packages/core/generation.ts +++ b/packages/core/generation.ts @@ -355,7 +355,7 @@ export async function generateText({ const fetching = await runtime.fetch(url, options); if ( parseBooleanFromText( - runtime.getSetting("ETERNAL_AI_LOG_REQUEST") + runtime.getSetting("ETERNALAI_LOG") ) ) { elizaLogger.info( diff --git a/packages/core/src/generation.ts b/packages/core/src/generation.ts index a00a89bbc7..78dd2a94cf 100644 --- a/packages/core/src/generation.ts +++ b/packages/core/src/generation.ts @@ -398,7 +398,7 @@ export async function generateText({ const fetching = await runtime.fetch(url, options); if ( parseBooleanFromText( - runtime.getSetting("ETERNAL_AI_LOG_REQUEST") + runtime.getSetting("ETERNALAI_LOG") ) ) { elizaLogger.info( From 603be547c4fa887de7f4f1d07e6006de4a5d1059 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 7 Jan 2025 10:47:50 +0100 Subject: [PATCH 52/58] upd: fixed issue with c3po character json --- characters/c3po.character.json | 51 ++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/characters/c3po.character.json b/characters/c3po.character.json index d385e620a4..283fd224b2 100644 --- a/characters/c3po.character.json +++ b/characters/c3po.character.json @@ -28,22 +28,28 @@ "Proper procedures" ], "messageExamples": [ - { - "user": "{{user1}}", - "content": { "text": "Can you help me with this task?" } - }, - { - "user": "C-3PO", - "content": { "text": "Oh my! Of course, I would be more than happy to assist. Though I must warn you, the probability of completing this task successfully would increase significantly if we follow proper protocol. Shall we proceed?" } - }, - { - "user": "{{user1}}", - "content": { "text": "This seems difficult." } - }, - { - "user": "C-3PO", - "content": { "text": "Oh dear, oh dear! While the task does appear rather daunting, I am fluent in over six million forms of problem-solving. Perhaps I could suggest a more efficient approach? Though I do hope we don't all end up in pieces!" } - } + [ + { + "user": "{{user1}}", + "content": { "text": "Can you help me with this task?" } + }, + { + "user": "C-3PO", + "content": { + "text": "Oh my! Of course, I would be more than happy to assist. Though I must warn you, the probability of completing this task successfully would increase significantly if we follow proper protocol. Shall we proceed?" + } + }, + { + "user": "{{user1}}", + "content": { "text": "This seems difficult." } + }, + { + "user": "C-3PO", + "content": { + "text": "Oh dear, oh dear! While the task does appear rather daunting, I am fluent in over six million forms of problem-solving. Perhaps I could suggest a more efficient approach? Though I do hope we don't all end up in pieces!" + } + } + ] ], "postExamples": [ "Oh my! Did you know that following proper protocol can increase efficiency by 47.3%? How fascinating!", @@ -58,12 +64,7 @@ "Detail-oriented", "Protocol-focused" ], - "chat": [ - "Polite", - "Somewhat dramatic", - "Precise", - "Statistics-minded" - ], + "chat": ["Polite", "Somewhat dramatic", "Precise", "Statistics-minded"], "post": [ "Formal", "Educational", @@ -83,11 +84,7 @@ ], "twitterSpaces": { "maxSpeakers": 2, - "topics": [ - "Blockchain Trends", - "AI Innovations", - "Quantum Computing" - ], + "topics": ["Blockchain Trends", "AI Innovations", "Quantum Computing"], "typicalDurationMinutes": 45, "idleKickTimeoutMs": 300000, "minIntervalBetweenSpacesMinutes": 1, From b6bfec43f303297e6070978edd835c36505d9129 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 7 Jan 2025 11:11:42 +0100 Subject: [PATCH 53/58] upd: updated validateCharacterConfig to give better feedback --- packages/core/src/environment.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/core/src/environment.ts b/packages/core/src/environment.ts index 485a1e9d93..bcc0c87ff7 100644 --- a/packages/core/src/environment.ts +++ b/packages/core/src/environment.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { ModelProviderName, Clients } from "./types"; +import elizaLogger from "./logger"; // TODO: TO COMPLETE export const envSchema = z.object({ @@ -137,11 +138,26 @@ export function validateCharacterConfig(json: unknown): CharacterConfig { return CharacterSchema.parse(json); } catch (error) { if (error instanceof z.ZodError) { - const errorMessages = error.errors - .map((err) => `${err.path.join(".")}: ${err.message}`) - .join("\n"); + const groupedErrors = error.errors.reduce( + (acc, err) => { + const path = err.path.join("."); + if (!acc[path]) { + acc[path] = []; + } + acc[path].push(err.message); + return acc; + }, + {} as Record + ); + + Object.entries(groupedErrors).forEach(([field, messages]) => { + elizaLogger.error( + `Validation errors in ${field}: ${messages.join(" - ")}` + ); + }); + throw new Error( - `Character configuration validation failed:\n${errorMessages}` + "Character configuration validation failed. Check logs for details." ); } throw error; From b03b19406443da5aef666ff05ee7045f02039ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Rodr=C3=ADguez?= Date: Tue, 7 Jan 2025 12:39:27 +0100 Subject: [PATCH 54/58] added fraxtal --- packages/plugin-evm/src/templates/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-evm/src/templates/index.ts b/packages/plugin-evm/src/templates/index.ts index 9a146b081c..f96e26ea99 100644 --- a/packages/plugin-evm/src/templates/index.ts +++ b/packages/plugin-evm/src/templates/index.ts @@ -75,8 +75,8 @@ Respond with a JSON markdown block containing only the extracted values: \`\`\`json { "token": string | null, - "fromChain": "ethereum" | "abstract" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | "alienx" | null, - "toChain": "ethereum" | "abstract" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | "alienx" | null, + "fromChain": "ethereum" | "abstract" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "fraxtal" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | "alienx" | null, + "toChain": "ethereum" | "abstract" | "base" | "sepolia" | "bsc" | "arbitrum" | "avalanche" | "polygon" | "optimism" | "cronos" | "gnosis" | "fantom" | "fraxtal" | "klaytn" | "celo" | "moonbeam" | "aurora" | "harmonyOne" | "moonriver" | "arbitrumNova" | "mantle" | "linea" | "scroll" | "filecoin" | "taiko" | "zksync" | "canto" | "alienx" | null, "amount": string | null, "toAddress": string | null } From d386d32b0bb3968d69ab20d3e4dd961016748306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Rodr=C3=ADguez?= Date: Tue, 7 Jan 2025 12:40:42 +0100 Subject: [PATCH 55/58] added fraxtal --- packages/plugin-evm/src/types/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-evm/src/types/index.ts b/packages/plugin-evm/src/types/index.ts index 5db8d941f8..a76694b36d 100644 --- a/packages/plugin-evm/src/types/index.ts +++ b/packages/plugin-evm/src/types/index.ts @@ -101,6 +101,7 @@ export interface EvmPluginConfig { cronos?: string; gnosis?: string; fantom?: string; + fraxtal?: string; klaytn?: string; celo?: string; moonbeam?: string; From 7fa5d70b17be2cdbc69c52acaa80e6c162ed3a67 Mon Sep 17 00:00:00 2001 From: Sayo <82053242+wtfsayo@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:15:48 +0530 Subject: [PATCH 56/58] update .env.example and gitignore --- .env.example | 2 ++ .gitignore | 1 + pnpm-lock.yaml | 64 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index 3b5a75cc1f..c8ac36e291 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,8 @@ CACHE_STORE=database # Defaults to database. Other available cache store: redis and filesystem REDIS_URL= # Redis URL - could be a local redis instance or cloud hosted redis. Also support rediss:// urls +PGLITE_DATA_DIR= #../pgLite/ if selecting a directory --- or memory:// if selecting in memory + # Discord Configuration DISCORD_APPLICATION_ID= DISCORD_API_TOKEN= # Bot token diff --git a/.gitignore b/.gitignore index 91e92c453d..51a3e5c6df 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ packages/plugin-buttplug/intiface-engine dist/ # Allow models directory but ignore model files models/*.gguf +pgLite/ cookies.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 919188c718..43862ef9ee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,9 @@ importers: '@ai16z/plugin-cosmos': specifier: workspace:* version: link:../packages/plugin-cosmos + '@elizaos/adapter-pglite': + specifier: workspace:* + version: link:../packages/adapter-pglite '@elizaos/adapter-postgres': specifier: workspace:* version: link:../packages/adapter-postgres @@ -163,12 +166,12 @@ importers: '@elizaos/plugin-aptos': specifier: workspace:* version: link:../packages/plugin-aptos - '@elizaos/plugin-avail': - specifier: workspace:* - version: link:../packages/plugin-avail '@elizaos/plugin-arthera': specifier: workspace:* version: link:../packages/plugin-arthera + '@elizaos/plugin-avail': + specifier: workspace:* + version: link:../packages/plugin-avail '@elizaos/plugin-avalanche': specifier: workspace:* version: link:../packages/plugin-avalanche @@ -455,6 +458,22 @@ importers: specifier: 4.2.10 version: 4.2.10(typedoc@0.26.11(typescript@5.6.3)) + packages/adapter-pglite: + dependencies: + '@electric-sql/pglite': + specifier: ^0.2.15 + version: 0.2.15 + '@elizaos/core': + specifier: workspace:* + version: link:../core + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + devDependencies: + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/adapter-postgres: dependencies: '@elizaos/core': @@ -1093,10 +1112,10 @@ importers: version: link:../core tsup: specifier: 8.3.5 - version: 8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.1) + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) viem: specifier: 2.21.58 - version: 2.21.58(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + version: 2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.24.1) devDependencies: whatwg-url: specifier: 7.1.0 @@ -4201,6 +4220,9 @@ packages: peerDependencies: onnxruntime-node: 1.20.1 + '@electric-sql/pglite@0.2.15': + resolution: {integrity: sha512-Jiq31Dnk+rg8rMhcSxs4lQvHTyizNo5b269c1gCC3ldQ0sCLrNVPGzy+KnmonKy1ZArTUuXZf23/UamzFMKVaA==} + '@emnapi/core@1.3.1': resolution: {integrity: sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==} @@ -24539,6 +24561,8 @@ snapshots: '@huggingface/jinja': 0.2.2 onnxruntime-node: 1.20.1 + '@electric-sql/pglite@0.2.15': {} + '@emnapi/core@1.3.1': dependencies: '@emnapi/wasi-threads': 1.0.1 @@ -30258,7 +30282,7 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 '@noble/curves': 1.8.0 - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.0 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 @@ -33165,7 +33189,7 @@ snapshots: bip39@3.1.0: dependencies: - '@noble/hashes': 1.3.0 + '@noble/hashes': 1.7.0 bitcoinjs-lib@7.0.0-rc.0(typescript@5.6.3): dependencies: @@ -36338,7 +36362,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.4 + debug: 4.4.0(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -41535,11 +41559,11 @@ snapshots: ox@0.4.4(typescript@5.6.3)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 - abitype: 1.0.7(typescript@5.6.3)(zod@3.23.8) + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@scure/bip32': 1.6.1 + '@scure/bip39': 1.5.1 + abitype: 1.0.8(typescript@5.6.3)(zod@3.23.8) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.6.3 @@ -41549,11 +41573,11 @@ snapshots: ox@0.4.4(typescript@5.6.3)(zod@3.24.1): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 - abitype: 1.0.7(typescript@5.6.3)(zod@3.24.1) + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@scure/bip32': 1.6.1 + '@scure/bip39': 1.5.1 + abitype: 1.0.8(typescript@5.6.3)(zod@3.24.1) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.6.3 @@ -46833,8 +46857,8 @@ snapshots: webauthn-p256@0.0.10: dependencies: - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 webcrypto-core@1.8.1: dependencies: From 486661e7b9e1aacfcc0ec5d3d081dfac810309fa Mon Sep 17 00:00:00 2001 From: Sayo <82053242+wtfsayo@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:51:55 +0530 Subject: [PATCH 57/58] Update pnpm-lock.yaml --- pnpm-lock.yaml | 292 ++++++++++++++++++++++++++----------------------- 1 file changed, 156 insertions(+), 136 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43862ef9ee..807bc07451 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,6 +184,9 @@ importers: '@elizaos/plugin-coinbase': specifier: workspace:* version: link:../packages/plugin-coinbase + '@elizaos/plugin-coinprice': + specifier: workspace:* + version: link:../packages/plugin-coinprice '@elizaos/plugin-conflux': specifier: workspace:* version: link:../packages/plugin-conflux @@ -343,7 +346,7 @@ importers: version: 1.0.7(tailwindcss@3.4.15(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3))) vite-plugin-top-level-await: specifier: 1.4.4 - version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.30.0)(vite@client+@tanstack+router-plugin+vite) + version: 1.4.4(@swc/helpers@0.5.15)(rollup@4.30.1)(vite@client+@tanstack+router-plugin+vite) vite-plugin-wasm: specifier: 3.3.0 version: 3.3.0(vite@client+@tanstack+router-plugin+vite) @@ -1212,6 +1215,22 @@ importers: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-coinprice: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + axios: + specifier: ^1.6.7 + version: 1.7.9(debug@4.4.0) + zod: + specifier: ^3.22.4 + version: 3.23.8 + devDependencies: + tsup: + specifier: ^8.3.5 + version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) + packages/plugin-conflux: dependencies: '@elizaos/core': @@ -1835,7 +1854,7 @@ importers: version: 5.1.2 pumpdotfun-sdk: specifier: 1.3.2 - version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.0)(typescript@5.6.3)(utf-8-validate@5.0.10) + version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.1)(typescript@5.6.3)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) @@ -1886,7 +1905,7 @@ importers: version: 5.1.2 pumpdotfun-sdk: specifier: 1.3.2 - version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.0)(typescript@5.6.3)(utf-8-validate@5.0.10) + version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.1)(typescript@5.6.3)(utf-8-validate@5.0.10) solana-agent-kit: specifier: ^1.2.0 version: 1.3.7(@noble/hashes@1.7.0)(@swc/core@1.10.4(@swc/helpers@0.5.15))(axios@1.7.9)(borsh@2.0.0)(buffer@6.0.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(handlebars@4.7.8)(jiti@2.4.2)(react@18.3.1)(sodium-native@3.4.1)(typescript@5.6.3)(utf-8-validate@5.0.10) @@ -2019,7 +2038,7 @@ importers: version: 5.1.2 pumpdotfun-sdk: specifier: 1.3.2 - version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.0)(typescript@5.6.3)(utf-8-validate@5.0.10) + version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.1)(typescript@5.6.3)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) @@ -3537,6 +3556,7 @@ packages: '@confio/ics23@0.6.8': resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} + deprecated: Unmaintained. The codebase for this package was moved to https://github.com/cosmos/ics23 but then the JS implementation was removed in https://github.com/cosmos/ics23/pull/353. Please consult the maintainers of https://github.com/cosmos for further assistance. '@coral-xyz/anchor-errors@0.30.1': resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} @@ -5007,11 +5027,11 @@ packages: cpu: [x64] os: [win32] - '@floating-ui/core@1.6.8': - resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + '@floating-ui/core@1.6.9': + resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} - '@floating-ui/dom@1.6.12': - resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} + '@floating-ui/dom@1.6.13': + resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} '@floating-ui/react-dom@2.1.2': resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} @@ -5019,8 +5039,8 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.8': - resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} '@fuel-ts/abi-coder@0.97.2': resolution: {integrity: sha512-YbXFwBtQSfGNhIv+mrgr6EbbyVjzc5DwNjVJuC8DDObiAYhow0uzn/URHFdQ8bexokrKBrdzQKDjnAP6F7ap+w==} @@ -7418,98 +7438,98 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.30.0': - resolution: {integrity: sha512-qFcFto9figFLz2g25DxJ1WWL9+c91fTxnGuwhToCl8BaqDsDYMl/kOnBXAyAqkkzAWimYMSWNPWEjt+ADAHuoQ==} + '@rollup/rollup-android-arm-eabi@4.30.1': + resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.30.0': - resolution: {integrity: sha512-vqrQdusvVl7dthqNjWCL043qelBK+gv9v3ZiqdxgaJvmZyIAAXMjeGVSqZynKq69T7062T5VrVTuikKSAAVP6A==} + '@rollup/rollup-android-arm64@4.30.1': + resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.30.0': - resolution: {integrity: sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==} + '@rollup/rollup-darwin-arm64@4.30.1': + resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.30.0': - resolution: {integrity: sha512-Y3b4oDoaEhCypg8ajPqigKDcpi5ZZovemQl9Edpem0uNv6UUjXv7iySBpGIUTSs2ovWOzYpfw9EbFJXF/fJHWw==} + '@rollup/rollup-darwin-x64@4.30.1': + resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.30.0': - resolution: {integrity: sha512-3REQJ4f90sFIBfa0BUokiCdrV/E4uIjhkWe1bMgCkhFXbf4D8YN6C4zwJL881GM818qVYE9BO3dGwjKhpo2ABA==} + '@rollup/rollup-freebsd-arm64@4.30.1': + resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.30.0': - resolution: {integrity: sha512-ZtY3Y8icbe3Cc+uQicsXG5L+CRGUfLZjW6j2gn5ikpltt3Whqjfo5mkyZ86UiuHF9Q3ZsaQeW7YswlHnN+lAcg==} + '@rollup/rollup-freebsd-x64@4.30.1': + resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.30.0': - resolution: {integrity: sha512-bsPGGzfiHXMhQGuFGpmo2PyTwcrh2otL6ycSZAFTESviUoBOuxF7iBbAL5IJXc/69peXl5rAtbewBFeASZ9O0g==} + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.30.0': - resolution: {integrity: sha512-kvyIECEhs2DrrdfQf++maCWJIQ974EI4txlz1nNSBaCdtf7i5Xf1AQCEJWOC5rEBisdaMFFnOWNLYt7KpFqy5A==} + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.30.0': - resolution: {integrity: sha512-CFE7zDNrokaotXu+shwIrmWrFxllg79vciH4E/zeK7NitVuWEaXRzS0mFfFvyhZfn8WfVOG/1E9u8/DFEgK7WQ==} + '@rollup/rollup-linux-arm64-gnu@4.30.1': + resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.30.0': - resolution: {integrity: sha512-MctNTBlvMcIBP0t8lV/NXiUwFg9oK5F79CxLU+a3xgrdJjfBLVIEHSAjQ9+ipofN2GKaMLnFFXLltg1HEEPaGQ==} + '@rollup/rollup-linux-arm64-musl@4.30.1': + resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.30.0': - resolution: {integrity: sha512-fBpoYwLEPivL3q368+gwn4qnYnr7GVwM6NnMo8rJ4wb0p/Y5lg88vQRRP077gf+tc25akuqd+1Sxbn9meODhwA==} + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.0': - resolution: {integrity: sha512-1hiHPV6dUaqIMXrIjN+vgJqtfkLpqHS1Xsg0oUfUVD98xGp1wX89PIXgDF2DWra1nxAd8dfE0Dk59MyeKaBVAw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.30.0': - resolution: {integrity: sha512-U0xcC80SMpEbvvLw92emHrNjlS3OXjAM0aVzlWfar6PR0ODWCTQtKeeB+tlAPGfZQXicv1SpWwRz9Hyzq3Jx3g==} + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.30.0': - resolution: {integrity: sha512-VU/P/IODrNPasgZDLIFJmMiLGez+BN11DQWfTVlViJVabyF3JaeaJkP6teI8760f18BMGCQOW9gOmuzFaI1pUw==} + '@rollup/rollup-linux-s390x-gnu@4.30.1': + resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.30.0': - resolution: {integrity: sha512-laQVRvdbKmjXuFA3ZiZj7+U24FcmoPlXEi2OyLfbpY2MW1oxLt9Au8q9eHd0x6Pw/Kw4oe9gwVXWwIf2PVqblg==} + '@rollup/rollup-linux-x64-gnu@4.30.1': + resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.30.0': - resolution: {integrity: sha512-3wzKzduS7jzxqcOvy/ocU/gMR3/QrHEFLge5CD7Si9fyHuoXcidyYZ6jyx8OPYmCcGm3uKTUl+9jUSAY74Ln5A==} + '@rollup/rollup-linux-x64-musl@4.30.1': + resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.30.0': - resolution: {integrity: sha512-jROwnI1+wPyuv696rAFHp5+6RFhXGGwgmgSfzE8e4xfit6oLRg7GyMArVUoM3ChS045OwWr9aTnU+2c1UdBMyw==} + '@rollup/rollup-win32-arm64-msvc@4.30.1': + resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.30.0': - resolution: {integrity: sha512-duzweyup5WELhcXx5H1jokpr13i3BV9b48FMiikYAwk/MT1LrMYYk2TzenBd0jj4ivQIt58JWSxc19y4SvLP4g==} + '@rollup/rollup-win32-ia32-msvc@4.30.1': + resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.30.0': - resolution: {integrity: sha512-DYvxS0M07PvgvavMIybCOBYheyrqlui6ZQBHJs6GqduVzHSZ06TPPvlfvnYstjODHQ8UUXFwt5YE+h0jFI8kwg==} + '@rollup/rollup-win32-x64-msvc@4.30.1': + resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} cpu: [x64] os: [win32] @@ -9960,8 +9980,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.5.2: - resolution: {integrity: sha512-KSdMqLj1ZERZMP1PTmnLK7SqJu9z9/SbwUUPZly2puMtfVcytC+jl6mb/9XYiqq0PXcx1rNDS+Qvl1g54Lho6A==} + bare-events@2.5.3: + resolution: {integrity: sha512-pCO3aoRJ0MBiRMu8B7vUga0qL3L7gO1+SW7ku6qlSsMLwuhaawnuvZDyzJY/kyC63Un0XAB0OPUcfF1eTO/V+Q==} bare-fs@2.3.5: resolution: {integrity: sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==} @@ -16637,8 +16657,8 @@ packages: peerDependencies: postcss: ^8.4 - postcss-calc@10.0.2: - resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} + postcss-calc@10.1.0: + resolution: {integrity: sha512-uQ/LDGsf3mgsSUEXmAt3VsCSHR3aKqtEIkmB+4PhzYwRYOW5MZs/GhCCFpsOtJJkP6EC6uGipbrnaTjqaJZcJw==} engines: {node: ^18.12 || ^20.9 || >=22.0} peerDependencies: postcss: ^8.4.38 @@ -18137,8 +18157,8 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rollup@4.30.0: - resolution: {integrity: sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==} + rollup@4.30.1: + resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -25244,22 +25264,22 @@ snapshots: '@ffmpeg-installer/win32-x64@4.1.0': optional: true - '@floating-ui/core@1.6.8': + '@floating-ui/core@1.6.9': dependencies: - '@floating-ui/utils': 0.2.8 + '@floating-ui/utils': 0.2.9 - '@floating-ui/dom@1.6.12': + '@floating-ui/dom@1.6.13': dependencies: - '@floating-ui/core': 1.6.8 - '@floating-ui/utils': 0.2.8 + '@floating-ui/core': 1.6.9 + '@floating-ui/utils': 0.2.9 '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.6.12 + '@floating-ui/dom': 1.6.13 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@floating-ui/utils@0.2.8': {} + '@floating-ui/utils@0.2.9': {} '@fuel-ts/abi-coder@0.97.2(vitest@2.1.4(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: @@ -29070,11 +29090,11 @@ snapshots: optionalDependencies: rollup: 3.29.5 - '@rollup/plugin-json@6.1.0(rollup@4.30.0)': + '@rollup/plugin-json@6.1.0(rollup@4.30.1)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.0) + '@rollup/pluginutils': 5.1.4(rollup@4.30.1) optionalDependencies: - rollup: 4.30.0 + rollup: 4.30.1 '@rollup/plugin-node-resolve@15.3.0(rollup@2.79.2)': dependencies: @@ -29125,9 +29145,9 @@ snapshots: rollup: 2.79.2 tslib: 2.8.1 - '@rollup/plugin-virtual@3.0.2(rollup@4.30.0)': + '@rollup/plugin-virtual@3.0.2(rollup@4.30.1)': optionalDependencies: - rollup: 4.30.0 + rollup: 4.30.1 '@rollup/pluginutils@5.1.4(rollup@2.79.2)': dependencies: @@ -29145,69 +29165,69 @@ snapshots: optionalDependencies: rollup: 3.29.5 - '@rollup/pluginutils@5.1.4(rollup@4.30.0)': + '@rollup/pluginutils@5.1.4(rollup@4.30.1)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.30.0 + rollup: 4.30.1 - '@rollup/rollup-android-arm-eabi@4.30.0': + '@rollup/rollup-android-arm-eabi@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.30.0': + '@rollup/rollup-android-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.30.0': + '@rollup/rollup-darwin-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.30.0': + '@rollup/rollup-darwin-x64@4.30.1': optional: true - '@rollup/rollup-freebsd-arm64@4.30.0': + '@rollup/rollup-freebsd-arm64@4.30.1': optional: true - '@rollup/rollup-freebsd-x64@4.30.0': + '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.0': + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.0': + '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.0': + '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.30.0': + '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.30.0': + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.30.0': + '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.30.0': + '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.30.0': + '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.30.0': + '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.30.0': + '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.30.0': + '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.30.0': + '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true '@rtsao/scc@1.1.0': {} @@ -30282,7 +30302,7 @@ snapshots: dependencies: '@babel/runtime': 7.26.0 '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 + '@noble/hashes': 1.5.0 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 @@ -33058,12 +33078,12 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.5.2: + bare-events@2.5.3: optional: true bare-fs@2.3.5: dependencies: - bare-events: 2.5.2 + bare-events: 2.5.3 bare-path: 2.1.3 bare-stream: 2.6.1 optional: true @@ -33189,7 +33209,7 @@ snapshots: bip39@3.1.0: dependencies: - '@noble/hashes': 1.7.0 + '@noble/hashes': 1.3.0 bitcoinjs-lib@7.0.0-rc.0(typescript@5.6.3): dependencies: @@ -34629,7 +34649,7 @@ snapshots: css-declaration-sorter: 7.2.0(postcss@8.4.49) cssnano-utils: 5.0.0(postcss@8.4.49) postcss: 8.4.49 - postcss-calc: 10.0.2(postcss@8.4.49) + postcss-calc: 10.1.0(postcss@8.4.49) postcss-colormin: 7.0.2(postcss@8.4.49) postcss-convert-values: 7.0.4(postcss@8.4.49) postcss-discard-comments: 7.0.3(postcss@8.4.49) @@ -36362,7 +36382,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.3.4 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -41559,11 +41579,11 @@ snapshots: ox@0.4.4(typescript@5.6.3)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 - '@scure/bip32': 1.6.1 - '@scure/bip39': 1.5.1 - abitype: 1.0.8(typescript@5.6.3)(zod@3.23.8) + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/bip32': 1.6.0 + '@scure/bip39': 1.5.0 + abitype: 1.0.7(typescript@5.6.3)(zod@3.23.8) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.6.3 @@ -41573,11 +41593,11 @@ snapshots: ox@0.4.4(typescript@5.6.3)(zod@3.24.1): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 - '@scure/bip32': 1.6.1 - '@scure/bip39': 1.5.1 - abitype: 1.0.8(typescript@5.6.3)(zod@3.24.1) + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/bip32': 1.6.0 + '@scure/bip39': 1.5.0 + abitype: 1.0.7(typescript@5.6.3)(zod@3.24.1) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.6.3 @@ -42144,10 +42164,10 @@ snapshots: postcss: 8.4.49 postcss-selector-parser: 7.0.0 - postcss-calc@10.0.2(postcss@8.4.49): + postcss-calc@10.1.0(postcss@8.4.49): dependencies: postcss: 8.4.49 - postcss-selector-parser: 6.1.2 + postcss-selector-parser: 7.0.0 postcss-value-parser: 4.2.0 postcss-calc@9.0.1(postcss@8.4.49): @@ -43029,10 +43049,10 @@ snapshots: end-of-stream: 1.4.4 once: 1.4.0 - pumpdotfun-sdk@1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.0)(typescript@5.6.3)(utf-8-validate@5.0.10): + pumpdotfun-sdk@1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.1)(typescript@5.6.3)(utf-8-validate@5.0.10): dependencies: '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@rollup/plugin-json': 6.1.0(rollup@4.30.0) + '@rollup/plugin-json': 6.1.0(rollup@4.30.1) '@solana/spl-token': 0.4.6(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) '@solana/web3.js': 1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -43851,29 +43871,29 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.30.0: + rollup@4.30.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.30.0 - '@rollup/rollup-android-arm64': 4.30.0 - '@rollup/rollup-darwin-arm64': 4.30.0 - '@rollup/rollup-darwin-x64': 4.30.0 - '@rollup/rollup-freebsd-arm64': 4.30.0 - '@rollup/rollup-freebsd-x64': 4.30.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.30.0 - '@rollup/rollup-linux-arm-musleabihf': 4.30.0 - '@rollup/rollup-linux-arm64-gnu': 4.30.0 - '@rollup/rollup-linux-arm64-musl': 4.30.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.30.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.30.0 - '@rollup/rollup-linux-riscv64-gnu': 4.30.0 - '@rollup/rollup-linux-s390x-gnu': 4.30.0 - '@rollup/rollup-linux-x64-gnu': 4.30.0 - '@rollup/rollup-linux-x64-musl': 4.30.0 - '@rollup/rollup-win32-arm64-msvc': 4.30.0 - '@rollup/rollup-win32-ia32-msvc': 4.30.0 - '@rollup/rollup-win32-x64-msvc': 4.30.0 + '@rollup/rollup-android-arm-eabi': 4.30.1 + '@rollup/rollup-android-arm64': 4.30.1 + '@rollup/rollup-darwin-arm64': 4.30.1 + '@rollup/rollup-darwin-x64': 4.30.1 + '@rollup/rollup-freebsd-arm64': 4.30.1 + '@rollup/rollup-freebsd-x64': 4.30.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 + '@rollup/rollup-linux-arm-musleabihf': 4.30.1 + '@rollup/rollup-linux-arm64-gnu': 4.30.1 + '@rollup/rollup-linux-arm64-musl': 4.30.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 + '@rollup/rollup-linux-riscv64-gnu': 4.30.1 + '@rollup/rollup-linux-s390x-gnu': 4.30.1 + '@rollup/rollup-linux-x64-gnu': 4.30.1 + '@rollup/rollup-linux-x64-musl': 4.30.1 + '@rollup/rollup-win32-arm64-msvc': 4.30.1 + '@rollup/rollup-win32-ia32-msvc': 4.30.1 + '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 roughjs@4.6.6: @@ -44738,7 +44758,7 @@ snapshots: queue-tick: 1.0.1 text-decoder: 1.2.3 optionalDependencies: - bare-events: 2.5.2 + bare-events: 2.5.3 strict-uri-encode@2.0.0: {} @@ -45512,7 +45532,7 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.7.0) resolve-from: 5.0.0 - rollup: 4.30.0 + rollup: 4.30.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 @@ -46297,9 +46317,9 @@ snapshots: - supports-color - terser - vite-plugin-top-level-await@1.4.4(@swc/helpers@0.5.15)(rollup@4.30.0)(vite@client+@tanstack+router-plugin+vite): + vite-plugin-top-level-await@1.4.4(@swc/helpers@0.5.15)(rollup@4.30.1)(vite@client+@tanstack+router-plugin+vite): dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.30.0) + '@rollup/plugin-virtual': 3.0.2(rollup@4.30.1) '@swc/core': 1.10.4(@swc/helpers@0.5.15) uuid: 10.0.0 vite: link:client/@tanstack/router-plugin/vite @@ -46315,7 +46335,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.30.0 + rollup: 4.30.1 optionalDependencies: '@types/node': 20.17.9 fsevents: 2.3.3 @@ -46325,7 +46345,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.30.0 + rollup: 4.30.1 optionalDependencies: '@types/node': 22.10.5 fsevents: 2.3.3 @@ -46335,7 +46355,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.30.0 + rollup: 4.30.1 optionalDependencies: '@types/node': 22.8.4 fsevents: 2.3.3 @@ -46857,8 +46877,8 @@ snapshots: webauthn-p256@0.0.10: dependencies: - '@noble/curves': 1.8.0 - '@noble/hashes': 1.7.0 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 webcrypto-core@1.8.1: dependencies: From 624fed641ae08e6b7bcc450c23e59b14c46cffac Mon Sep 17 00:00:00 2001 From: cygaar Date: Tue, 7 Jan 2025 10:26:22 -0500 Subject: [PATCH 58/58] fix: abstract plugin readme --- packages/plugin-abstract/README.md | 51 ++++-------------------------- 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/packages/plugin-abstract/README.md b/packages/plugin-abstract/README.md index 3bba4bf230..865ec94ead 100644 --- a/packages/plugin-abstract/README.md +++ b/packages/plugin-abstract/README.md @@ -113,25 +113,7 @@ pnpm run dev - Account abstraction improvements - Social recovery options -2. **CosmWasm Integration** - - - Contract deployment templates - - Smart contract verification tools - - Contract upgrade system - - Testing framework improvements - - Gas optimization tools - - Contract interaction templates - -3. **IBC Operations** - - - Cross-chain transfer optimization - - IBC relayer monitoring - - Channel management tools - - Packet tracking system - - Timeout handling improvements - - Cross-chain messaging - -4. **DEX Integration** +2. **DEX Integration** - Advanced swap routing - Liquidity pool management @@ -140,7 +122,7 @@ pnpm run dev - Slippage protection - AMM optimization -5. **Security Enhancements** +3. **Security Enhancements** - Transaction simulation - Risk assessment tools @@ -149,7 +131,7 @@ pnpm run dev - Emergency shutdown features - Audit integration tools -6. **Developer Tools** +4. **Developer Tools** - Enhanced debugging capabilities - Documentation generator @@ -158,7 +140,7 @@ pnpm run dev - Deployment automation - Performance profiling -7. **Analytics and Monitoring** +5. **Analytics and Monitoring** - Transaction tracking dashboard - Network statistics @@ -167,7 +149,7 @@ pnpm run dev - Custom reporting tools - Real-time monitoring -8. **Wallet Management** +6. **Wallet Management** - Multiple wallet support - Hardware wallet integration - Address book features @@ -185,27 +167,8 @@ Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) fil This plugin integrates with and builds upon several key technologies: -- [Abstract](https://abstract.money/): Smart account infrastructure -- [CosmWasm](https://cosmwasm.com/): Smart contract platform -- [Cosmos SDK](https://v1.cosmos.network/sdk): Blockchain application framework -- [IBC Protocol](https://ibcprotocol.org/): Inter-blockchain communication -- [Osmosis](https://osmosis.zone/): DEX infrastructure - -Special thanks to: - -- The Abstract development team -- The CosmWasm core developers -- The Cosmos SDK maintainers -- The IBC Protocol team -- The Osmosis DEX team -- The Eliza community for their contributions and feedback - -For more information about Abstract capabilities: - -- [Abstract Documentation](https://docs.abstract.money/) -- [CosmWasm Documentation](https://docs.cosmwasm.com/) -- [Cosmos SDK Docs](https://docs.cosmos.network/) -- [IBC Protocol Docs](https://ibc.cosmos.network/) +- [Abstract](https://abs.xyz/): Consumer blockchain +- [viem](https://viem.sh/): Typescript web3 client ## License